Use¶
List every chunk under a path:
ish src/
src/ish/domain/chunk.py:7-28 class Chunk
Search for a query:
ish "parse a python file" src/
[0.71] src/ish/adapters/parser/python.py:14-31 method PythonParser.parse
Run the interactive picker and open the selection in your editor. Type to
search, up/down or ctrl+p/ctrl+n to move, enter to choose, ctrl+t
to change the theme, escape to quit. The picker lists what the index held
last time before it looks for changes, and answers from that while the
refresh runs, with the progress in the header. Narrow without leaving the
query line:
state machine transitions every language
lang:cpp state machine transitions the implementation
lang:yaml under:/system/ state the tests that cover it
type:doc how do I configure this the prose, not the code
type:test,doc retry backoff the tests and what they document
Press Tab to finish a filter word, in the picker and in Neovim alike. ty
becomes type:, lang:cp becomes lang:cpp, and under:/s becomes
under:/src/; a word with several answers grows as far as they agree and
names the rest. ish-complete does the work from the command line, and
complete_filter does it over MCP, so any picker can call whichever it has.
The shell finishes the same words on the command line. Source
contrib/shell/ish.bash from ~/.bashrc, or copy contrib/shell/_ish to a
directory on zsh's $fpath, and ish lang:cp<Tab> becomes ish lang:cpp.
Both call ish-complete, so the shell learns nothing about the languages.
lang:, under:, and type: work in the query line of every interface —
the command line, the picker, Neovim, and MCP. The words are taken out
before the query is embedded, so the model sees the question rather than
how it was narrowed.
A language may be named however it comes to mind. c, c++, cxx, h, and
hpp all mean cpp, because one parser reads them all; adoc means
asciidoc, md means markdown, py means python, and yml means yaml.
type: sorts every chunk into exactly one kind. A path decides before a
language does, so a YAML fixture counts as a test rather than as config:
| kind | what it holds |
|---|---|
code |
Python, C, C++, and anything a plugin parser adds |
doc |
Markdown and AsciiDoc |
test |
anything under tests/, spec/, fixtures/, plus test_* and conftest.py |
config |
YAML, JSON, and TOML outside a test path |
A repository that names its trees its own way can say so, in
.ish/config.toml:
type_patterns = [
"test:/[0-9_]*(Tests|Verification)/",
"doc:/[0-9.]*Specification/",
]
The first match wins; anything unmatched keeps the reading above.
A config beside a subtree adds to the one above it, so a tree settles only what it names and inherits the rest. Searching inside an already-indexed tree reads that tree's index and narrows the answers to the path, rather than starting a second index of the same files.
nvim $(ish -i src/)
Ask a question¶
--ask answers the query as a question, from the chunks a search finds:
ish --ask "how does the release branch get its version" docs/
ish prints Markdown a person reads from the top down: a short answer, a section for each part of the question, numbered steps for a procedure, a table for registers, addresses, or values, and one block for what the documents leave open. Each claim carries a numbered citation, and each source is a link that opens the file at its lines:
# how do I trigger a software reset
**Short answer:** write 0x01 to CTRL_CMD, then 0x1 to CTRL_GO.[1][2]
### Procedure
1. Set CTRL_CMD to 0x01, the software reset command.[1]
2. Set CTRL_GO to 0x1 to trigger it.[1][2]
> **Not in the documents**
>
> - The register addresses are in an appendix the sources do not hold.[1]
## Sources
1. [startup.adoc:329-364](file:///srv/docs/spec/startup.adoc#L329-364)
doc · Function Description > Reset By Communication
spec/
2. [reset.yaml:21-61](file:///srv/docs/tests/reset.yaml#L21-61)
test · Verify Reset By Communication
tests/
It reads well in a terminal, and an editor shows it as a document:
ish --ask "how does the release branch get its version" docs/ | nvim -c 'set ft=markdown' -
In Neovim, gx on a link opens the source. With link_base set, the
links point at the published tree instead of the files on disk.
A language model writes the answer, and it reads only the chunks the search
returned. When they do not answer the question, ish says so, lists the
closest sources, and exits 2. It does not answer from anything else. The
filter words work in a question as in a query: --ask "type:doc how is X
done".
For the fact alone, ask with --brief. The model writes a sentence or two
and nothing else, and ish prints it as plain text with no citations, which
suits a script or a shell prompt:
ish --brief "how long is the watchdog period while streaming" docs/
Ask again without --brief for the sources. A brief answer costs about a
third of the output tokens and half the time.
Answers are off until you choose a model. See Answer questions. A plain search never builds a language model and sends no text anywhere.
Options¶
| Flag | Purpose |
|---|---|
-i, --interactive |
Run the TUI picker |
-c, --config PATH |
Read the options from this file, not from the search upward |
--embedder {llama.cpp,ollama,st} |
Select the embedding backend (default: ollama) |
-v, -vv |
Increase log detail |
--color {auto,always,never} |
Control log color |
--limit N |
Maximum search results |
--ignore DIR ... |
Directory names to skip (default .git .venv venv __pycache__) |
--include REGEX ... |
Index only paths matching these patterns, anchored at the tree root |
--exclude REGEX ... |
Never index paths matching these patterns |
--git, --no-git |
Skip files git ignores (default: on) |
--unignore REGEX ... |
Index these paths although git ignores them, anchored at the tree root |
--lang LANG ... |
Show results only from these languages |
--under REGEX |
Show results only from matching paths |
--type TYPE ... |
Show results only of these kinds: code, doc, test, config |
--type-patterns TYPE:REGEX ... |
Say what a path holds, overriding the built-in reading |
--max-chunks N |
Index a file that yields more chunks than this as one chunk (default 1000) |
--context-tokens N |
How many tokens the model reads of each chunk (default 2048) |
--model NAME |
Override the backend model |
--refresh |
Bring every stored index at or below the path up to date first |
--reindex |
Discard the stored index and build it again |
--forget |
Remove the stored index of the path and stop. The vectors stay |
--sweep |
Delete the vectors no index of the path's repository references, and stop |
--no-cache |
Index in memory only, leaving nothing on disk |
--tui-theme NAME |
Theme for the picker (default: the Textual default) |
--ask |
Answer the query as a question, with the sources it cites |
--brief |
Answer the query in a sentence or two, with no sources |
--language-model NAME |
Language model backend that writes answers: anthropic, ollama, claude-cli |
--answer-model MODEL |
Model that backend runs. Required, there is no default |
--allowed-language-models NAME ... |
Backends that may receive text. Empty allows none |
--rerank-model MODEL |
Model of the same backend that picks the chunks an answer reads |
--rerank-candidates N |
Chunks the rerank model chooses from (default 50) |
--ask-turns N |
Follow-up searches the model may ask for before it answers (default 0) |
--prompt-dir DIR |
Instruction files that replace the shipped ones, file by file |
--link-base URL |
Base URL where the tree is published, for the links of an answer |
Logs go to stderr, so you can pipe stdout safely. A file that cannot be read
or parsed is counted in one line; -v names them.
Theme the picker¶
ctrl+t steps to the next theme while the picker runs and names the one it
lands on. The choice lasts for that run. Say where the next run starts with
tui_theme, in the config file or as --tui-theme NAME:
tui_theme = "solarized-light"
Textual holds a theme for a light terminal as well as for a dark one, and
the preview follows: it reads the source in gruvbox-light under a light
theme and in gruvbox-dark under a dark one. A name no theme answers to is
reported in the picker, which then keeps the default.
NO_COLOR still wins over all of it. Set it and the picker draws in
monochrome, the preview included, whatever theme is chosen.