Docs / Developer mode
Code intelligence
ivralo reads code through the language servers your project declares, a code index that caches what they and git say, and a set of read tools that answer without filling a small context window. This page covers declaring a server, the tools, the index, editing by symbol, and the exploration budget.
Declaring a language server
A language server knows which login a call reaches, every real use of it, its type and what the checker says about a file. ivralo ships no list of servers and starts nothing on its own: it asks the servers your project declares in IVRALO.md, one line per set of file suffixes.
# Language servers
- py: pyright-langserver --stdio
- ts=typescript,tsx=typescriptreact,js=javascript: typescript-language-server --stdio
Suffixes come with or without their dot. suffix=id names the language identifier the server is told a file is; without it the suffix itself is sent. A longer suffix (d.ts) wins over a shorter one, and a suffix on two lines stays with the first. Name the server by a program on your PATH or a path inside the repository.
Trust is the approval. Starting a server runs a program, and a cloned repository brings its own IVRALO.md, so nothing it declares starts until you trust the folder. Once you have, the first question about a .py file starts the py line's server with no second question, in the session, in ivralo ask, in a schedule and in ivralo index --code alike. The shell policy still judges the command first, and a deny entry refuses it. Read the # Language servers lines before you trust a folder: they will run.
The server runs in ivralo's own process tree, in the repository, with the shell's environment, and ends when ivralo does. A crashed server is restarted once; after a second crash the tools say so for the rest of the session.
| Setting | Default | Effect |
|---|---|---|
IVRALO_LSP_TIMEOUT | 30 | Seconds one request to a server may take |
IVRALO_LSP_WARMUP_WAIT | 15 | Seconds to wait for a server that is still indexing |
ivralo doctor has a language servers section: each line, whether its command is on PATH, what your policy says about starting it, and for a server it may start, its version, what it can answer and how long it took to warm up.
The language-server tools
| Tool | Answers |
|---|---|
goto_definition | Where the symbol at a position is defined |
find_references | Every use of it, counted first; within= narrows, limit (at most 200) lengthens |
find_implementations | The classes or functions that implement an interface or abstract method |
call_hierarchy | Who calls a function, or what it calls, one level |
hover_info | The symbol's type or signature, then its documentation |
code_diagnostics | The errors and warnings the server reports for a file, errors first |
rename_symbol | A rename at every site the server finds; an edit, shown before it lands |
organize_imports | The server's own organize-imports action; an edit |
A position is a line and the name on it (symbol="login"), or a 1-based column. What a server cannot answer is said, never returned as an empty list: a suffix no line covers, a question the server does not support, a server still warming up.
The code index
The code index is a cache in .ivralo/index/index.sqlite of what your language server and git say about the repository. It parses nothing itself. It stores declarations (classes, functions, methods, module-level names, class fields), not the variables inside a function.
| Tool | Answers |
|---|---|
symbol_search | Where a name is defined: exact names first, then prefixes, then names holding it; source before tests |
file_outline | One file's symbols with their line ranges, so the agent reads only the lines it needs |
Every answer opens with how much of the repository is indexed and at which commit, and a hit whose file changed since is marked stale. Each turn refreshes the index for at most IVRALO_CODE_INDEX_BUDGET seconds (2 by default; 0 turns it off), so a large repository fills over several turns and never holds one up. To pay the whole cost at once:
ivralo index --code # read every file now
ivralo index --code --rebuild # start over
ivralo index --code --semantic # also embed code for semantic_code_search
It is a cache: deleting .ivralo/index/ costs a rebuild and nothing else. ivralo doctor says which commit it reflects and how far behind HEAD that is.
The read surface
A third set of tools lets an agent read a repository without reading whole files into its context. Each is a query over the index, the language server, git or the knowledge index; none is a parser. Each answer puts its count first and says when its source is stale or missing.
| Tool | Answers |
|---|---|
repo_summary | What IVRALO.md declares, files by suffix, the top-level layout, the files that change most, the README's first paragraph |
repo_tree | Directories first with their file counts, a few levels deep |
related_files | Files that change in the same commits, use the file's symbols, or share its name, each with why |
related_tests | The tests that reference a symbol, then the file's related tests |
inheritance_graph | A type's supertypes and subtypes |
dead_code_candidates | Symbols nothing in the workspace references; candidates only, nothing deleted |
code_slice | One symbol as a package: its code, its class line, the imports it uses, the signatures of what it calls, its test, within IVRALO_SLICE_BUDGET tokens (2,000); the header lists what was left out |
semantic_code_search | Code by what it does ("where is the token validated"), cited by file, lines and symbol |
semantic_code_search embeds code a little at each call, or all of it with ivralo index --code --semantic. With a hosted embedding model it refuses, so code does not leave the machine, unless IVRALO_EMBED_CODE_HOSTED=1.
Editing by symbol
Where a language server is configured, the agents can change code by naming what they change instead of copying it into edit_file:
| Tool | Does |
|---|---|
replace_symbol(path, symbol, new_text, line=0) | Replace one function, method, class or constant |
insert_before_symbol, insert_after_symbol | Add lines just before or after a symbol |
delete_symbol(path, symbol, line=0) | Delete a symbol, only when nothing else refers to it |
add_import(path, statement) | Add an import at the end of the import block, once |
replace_range(path, start_line, end_line, new_text, expected_text) | Replace lines that are exactly the text expected, for a file no server covers |
format_file(path), format_changed_files() | Run your project's own formatter |
A symbol is named as file_outline shows it (login, or Session.login), and line settles an ambiguous name. Every edit needs the file read first; if the symbol's lines changed since, it stops with PATCH_CONFLICT. Every other byte of the file stays as it was.
After an edit lands, the server's errors for the file are compared with those before it, and a new one is reported. IVRALO_EDIT_REFUSES_ON_DIAGNOSTIC=1 rolls the edit back instead.
ivralo never formats with a tool of its own. Declare yours:
# Commands
- format-paths: `uv run ruff format {paths}`
format-paths gets the files in its {paths} slot. With only format declared, the formatter runs over the whole workspace; with neither, nothing is formatted.
write_file on a file that exists is refused for the coder, with the symbol edits named instead; IVRALO_WHOLE_FILE_WRITES=1 allows it. All symbol edits ask like any edit, and edit_file in allow_tools covers them unattended (Editing and approvals).
The exploration budget
Every read tool has an exploration level, from cheap to expensive, and the coding skill reads in that order:
| Level | Tools |
|---|---|
| 1 | repo_summary, repo_tree, glob_files |
| 2 | symbol_search, file_outline, grep_files, related_files, related_tests |
| 3 | code_slice, goto_definition, find_references, find_implementations, call_hierarchy, inheritance_graph, hover_info, semantic_code_search |
| 4 | read_workspace_file |
What a turn spent exploring is counted and shown beside the context figure, explored 3.1k tokens (level 2), and in the turn summary and ivralo ask --format json. By default it is advice only. Set a budget and it is enforced:
IVRALO_EXPLORATION_BUDGET=20000 # tokens per specialist per turn; 0 = advice only
IVRALO_EXPLORATION_FILE_LINES=200 # the widest read never refused
With a budget on, a read of more than 200 lines is refused until the agent has an outline of that file this turn, and past the budget a level-3 call or such a wide read is refused too. Each refusal names the cheaper call. The budget yields wherever the cheap levels cannot answer: no language server declared, no symbols for the file, a file git does not list.
Whatever the budget, the same read asked twice in one run, with nothing written in between, is answered "you already have this" instead of running again.