# Installing the package puts a command called fix on your path

> fish-ai is a Fish shell plugin that turns comments into commands and commands into comments, repairs broken commands and autocompletes through a fuzzy finder, backed by a dozen model providers. Its Python distribution installs nine console entry points, two of them named fix and refine, and its configuration selects some providers implicitly and others explicitly.

**Realiserad/fish-ai** — Supercharge your command line with LLMs and get shell scripting assistance in Fish. 💪

- Repository: https://github.com/Realiserad/fish-ai
- Stars: 563 · Forks: 47
- Language: Python
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/realiserad-fish-ai

## Installing the package puts a command called fix on your path

The plugin itself is a handful of Fish files, installed with a plugin manager.

```shell
fisher install realiserad/fish-ai
```

Underneath, there is a Python distribution, and its manifest declares nine console entry points. Two of them are named `fix` and `refine`, and the others are `codify`, `explain`, `autocomplete`, `switch_context`, `put_api_key`, `lookup_setting` and `put_setting`.

```text
fix = "fish_ai.fix:fix"
refine = "fish_ai.autocomplete:refine_completions"
```

So installing this package puts a bare `fix` on your PATH and a bare `refine` beside it, with no project name attached. The manifest is candid that this is the core library rather than the product, and the plugin side lives in the shell directories of the repository, so the Python distribution and the thing you run in a terminal are two halves of one project with two different sets of names.

## Some providers are selected by section name, others explicitly

The configuration is an ini file, by default under the XDG config directory. What is inconsistent is how a provider gets picked.

Several examples declare a section and nothing else.

```ini
[deepseek]
provider = deepseek
api_key = <your API key>
model = deepseek-chat
```

Others require a selector in a top-level section that names which block to use, for example `configuration = azure` alongside an `[azure]` block, or `configuration = self-hosted` alongside `[self-hosted]`, or `configuration = local-llama` alongside a `[local-llama]` block. The Anthropic, Cohere, DeepSeek, Google and Groq examples work without the selector, while Azure, both AWS paths, OpenAI, OpenRouter, OrcaRouter and the self-hosted ones do not.

A reader who copies the first example they see and then adds a different provider has to work out which of the two conventions applies. The documentation does not mention the difference, and it contains at least one other editing slip in the sentence introducing the file.

## A subscription path depends on another vendor's command line tool

The Anthropic section has two routes, and the second one is unusual.

```ini
[anthropic]
provider = anthropic
use_subscription = True
```

Setting that flag uses a Claude subscription rather than an API key, and the documentation states that it requires the Claude Code CLI to be installed. So one provider path in this plugin depends on a separate command line product from the same vendor being present on the machine.

By contrast the DeepSeek section uses a personal access token created from the token settings page, and the documentation notes that the token requires no permissions at all. That is a very different security posture from an API key with billing attached, and the only place it is mentioned is one line under that provider.

Both are sensible choices. Neither is flagged in a summary, so the difference only becomes visible if you read to the end of the provider list.

## Two ways into Bedrock, two different permissions

AWS is the only vendor given two complete configurations, and the two paths need different permissions from each other.

The Converse API path sets a key naming that API, and the documentation states that it requires the permission to invoke models on Bedrock. The alternative, a gateway path, requires a permission in a different namespace entirely, the one belonging to that gateway's inference service. The two examples also differ in how the model is named, one using a versioned model identifier with a date and a version suffix, the other a bare model name.

Credentials work three ways. You can pass an API key directly, you can name an AWS profile and have that profile's credentials used, or you can leave both out and let the default credential chain resolve. When no key is configured, a short-term token is generated automatically, which means a local session with ambient AWS credentials will authenticate without you configuring anything at all.

That last behaviour is worth understanding before you point the plugin at an account you did not choose.

## A hosted router configured as a self-hosted server

The OpenRouter example sets its provider to the self-hosted type and points the server at the vendor's own URL, with a request body override carrying reasoning settings.

```ini
[openrouter]
provider = self-hosted
server = https://openrouter.ai/api/v1
```

That is not necessarily a mistake, since the type describes the wire protocol rather than who operates the endpoint, and OpenRouter speaks an OpenAI-compatible API. But a reader scanning the provider column sees self-hosted next to a public hostname, which is confusing in a file where the real self-hosted example is ten lines below.

The genuine self-hosted example is the recommended local setup, and it has no key at all: a server address on the loopback interface, a model name, and the author's recommendation to run it through Ollama with a large open model. Nothing is sent anywhere, which is the strongest privacy story in the configuration file.

## A binary file detector sits in the dependency list

The runtime dependencies are pinned to exact versions, eleven of them, and two entries explain behaviour the README never mentions.

One is a library that detects whether a file is binary. Nothing in the documentation says what it is used for, and it is not needed to talk to any model provider, so its presence implies the plugin checks what it is about to send and refuses or skips non-text input. For a tool whose job is to send your terminal history and command lines to a hosted model, that is a sensible default and an undocumented one.

The other is the HTTP client, pinned with an extra that enables SOCKS proxy support, which matters for anyone behind a proxy.

The rest are the provider clients: one SDK each for OpenAI, Anthropic, Google and Groq, plus the AWS SDK and a separate token generator for Bedrock. A terminal menu library and a Fish fuzzy-finder binding supply the interactive parts, and a system keyring library backs the alternative to storing keys in the configuration file.

## Two thousand lines, and the author invites you to count

The pitch is short and specific. The plugin turns a comment into a shell command and a command back into a comment, which saves reading manual pages and copying from question sites, particularly for tools with many flags. It repairs a broken command in the spirit of an existing fix-command tool, and it autocompletes what you meant to type through a fuzzy finder. Two configurable keyboard shortcuts do all of it, with no mouse.

It also lists what it will not do: it does not wrap your shell, install telemetry, or require you to switch terminal emulator, and it is tested not to interfere with two popular Fish plugins or the others you already load.

The author describes the codebase as around two thousand lines and says you can audit it in an afternoon, which is a claim you can check yourself. The provenance is credited too: the plugin began as someone else's project, and the README says plainly that it would not exist without that person. The maintenance line is equally plain, with the project described as largely feature complete and contributors asked to open an issue before proposing a feature.

## A Portuguese README, an asciinema recording and one whitelisted typo

The repository is more than the plugin. It carries a second README in Portuguese, an architecture document, a development guide, a recorded terminal demo in a text cast format, and a lint configuration for Markdown.

The tooling is spelled out in the manifest as well. Line length is capped at eighty characters, a type checker runs in a basic mode, and the test paths point inside the package rather than a separate tests directory, so the tests ship with the code. Development dependencies include a coverage tool, a test runner, a fake filesystem library and a mutation testing tool, which is a serious testing setup for a shell plugin.

One configuration detail is almost too small to mention: the spell checker used in development has an extended word list containing a single entry, added because that word is a package name it kept flagging. It is the sort of thing that tells you how the project is maintained, one word at a time.

## Conclusion

Try it in a Fish shell if you spend your day writing commands, and read the configuration section before you do, because the provider examples are not uniform and two of them need something other than an API key. Before installing the Python package, notice what it puts on your PATH: a command named fix and one named refine, with nothing in the name to tell you which project they belong to.

## FAQ

### What does fish-ai add to the fish shell?

It turns a comment into a shell command and a shell command into a comment, repairs a broken command, and autocompletes commands through a fuzzy finder. Everything is driven by two configurable keyboard shortcuts, with no mouse needed.

### How do I install fish-ai?

With the fisher plugin manager, by running `fisher install realiserad/fish-ai`. You need git installed, plus either uv or a supported Python version, together with pip and venv.

### Which model providers does fish-ai support?

Anthropic, Azure OpenAI, AWS Bedrock through either the Converse API or the Mantle gateway, Cohere, DeepSeek, Google, Groq, OpenAI, OpenRouter, OrcaRouter, and any self-hosted server exposing an OpenAI-compatible API.

### Can fish-ai use a Claude subscription instead of an API key?

Yes. Setting use_subscription = True in the anthropic section uses a subscription, and the documentation says it requires the Claude Code CLI to be installed. Otherwise you provide an API key in the configuration file or on the system keyring.

### Where does fish-ai keep its configuration?

In an ini file at $XDG_CONFIG_HOME/fish-ai.ini, or at ~/.config/fish-ai.ini when that variable is not set. You can keep API keys in that file or put them on the system keyring instead.

## Sources

- [Issues](https://github.com/Realiserad/fish-ai/issues)
- [License: MIT](https://github.com/Realiserad/fish-ai/blob/main/LICENSE)
- [README](https://github.com/Realiserad/fish-ai/blob/main/README.md)
- [Realiserad/fish-ai on GitHub](https://github.com/Realiserad/fish-ai)
- [Releases](https://github.com/Realiserad/fish-ai/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/realiserad-fish-ai
