Advanced Features¶
YAICLI includes several advanced features to enhance your interaction with AI models.
Chat Persistent Sessions¶
YAICLI allows you to save and manage chat sessions:
# Start a temporary chat session
$ ai --chat
# Start a persistent chat session with a title
$ ai --chat "Python programming help"
# List saved chat sessions
$ ai --list-chats
Managing Chat Sessions¶
In a chat session, you can use these commands:
- /save <title> - Save the current session with a title
- /list - List all saved sessions
- /load <index> - Load a previously saved session
- /del <index> - Delete a saved session
Persisting Chat Sessions¶
When you start a chat with a title, the session is automatically saved to disk:
$ ai --chat "check disk usage"
Chat title: check disk usage
The session will be persisted to CHAT_HISTORY_DIR by default.
Role Management¶
YAICLI supports custom roles to guide AI behavior:
# Create a new role
ai --create-role "Python Expert"
# List all roles
ai --list-roles
# Show a role
ai --show-role "Python Expert"
# Delete a role
ai --delete-role "Python Expert"
# Use a specific role
ai --role "Python Expert" "How do I use decorators?"
Function Calling¶
YAICLI supports function calling: the model can ask YAICLI to execute a local
function/tool (for example fs_read_file, web_search, or execute_shell_command)
and then continue the conversation with the result.
Built-in tools load out of the box¶
Since v0.19 the 12 built-in tool definitions are loaded directly from the installed package, so
the model can call them on the first run with no setup step. ai --install-functions is no longer
required (it remains available as an export command, see below).
Loading precedence:
- the built-in definitions shipped in the package;
- then Python files in the functions directory (
<config-dir>/functions/, normally~/.config/yaicli/functions/); a user file whose tool name matches a built-in overrides it. - A broken function file only disables that file: a warning is printed to stderr and the other tools keep working.
| Action | Command | Effect |
|---|---|---|
| List tools | ai --list-functions |
Prints every loaded tool with its source (builtin/user) and warns about stale copies. |
| Export editable copies | ai --install-functions |
Copies every built-in definition into the functions directory. Existing files are skipped, so your customisations are never overwritten. |
| Refresh exported copies | ai --reinstall-functions |
Overwrites files matching current built-ins with the new version; custom files (different names) are preserved. |
When do I need ai --reinstall-functions? Only when you have exported copies in the functions
directory and want the definitions from the new version. If you never ran --install-functions,
an upgrade needs no extra step. A stale exported copy is reported once by --list-functions, and
the built-in stays active for tool names you did not customise.
Paths on Windows: %APPDATA%\yaicli\functions when APPDATA is set, otherwise
C:\Users\<user>\.config\yaicli\functions (the non-Windows path is
~/.config/yaicli/functions/).
Writing custom functions¶
Add a Python file to ~/.config/yaicli/functions/ (files starting with _ are ignored).
It must define a class named Function that subclasses yaicli.function_schema.OpenAISchema
and a classmethod execute. The tool name is the pydantic model title (class Config: title).
Pydantic fields become the tool parameters; use Field(description=...) (and a class
docstring) so the model knows how to call it. Example:
from pydantic import Field
from yaicli.function_schema import OpenAISchema
class Function(OpenAISchema):
"""Add two integers and return the result."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
class Config:
title = "add_numbers"
@classmethod
def execute(cls, a: int, b: int) -> str:
return str(a + b)
Restart ai after adding or changing a custom function; the loader executes every
*.py file in that directory at startup. ai --reinstall-functions never deletes custom
files, and ai --install-functions only adds built-in files that are missing.
Enabling and controlling function calls¶
- Function calling is enabled by default (
ENABLE_FUNCTIONS=true). Toggle it per run with--enable-functions/--disable-functions, or setENABLE_FUNCTIONS=falsein the config file. - Function output is displayed by default; use
--hide-function-outputor setSHOW_FUNCTION_OUTPUT=falseto hide it. Long output is truncated in the terminal exactly as it is in the model context (see Context & Limits). - Every call asks for confirmation by default (
TOOL_CONFIRM=true); see Permissions & Safety for the prompt, risk levels, rules and audit log. MAX_TOOL_CALL_DEPTH(default8) bounds recursive tool-call rounds in one request; override per run with--max-turns N.
Reading Multiple Files in One Call¶
The built-in fs_read_file function accepts a list of file paths, so several
files can be read with a single tool call instead of one call per file:
{"file_paths": ["src/main.py", "src/config.py"], "encoding": "utf-8"}
A single path string is still accepted for backward compatibility. If you keep exported copies of
the built-in definitions, refresh them with ai --reinstall-functions after upgrading.
Reading Multiple Images in One Call¶
The built-in fs_read_image function follows the same list-based contract, so several
images can be read and base64-encoded in one tool call:
{"image_paths": ["artwork/before.png", "artwork/after.png"]}
A single path string is still accepted for backward compatibility. The same
ai --reinstall-functions rule applies to exported copies.
Built-in Function Safety¶
The built-in functions run with your local permissions, so the shipped implementations apply
their own guard rails. These are user-visible contracts — if you see different behaviour,
run ai --reinstall-functions after upgrading.
fetch_webpage only fetches public http/https URLs:
- loopback, private, link-local, cloud-metadata (
169.254.169.254,100.100.100.200), CGNAT/shared-address (100.64.0.0/10), multicast, reserved, and unspecified addresses are refused before any request is made (IPv4 and IPv6); - redirects are followed manually and re-validated hop by hop (maximum 5 redirects); a
cross-host redirect does not forward your cookies or
Referer, and anhttps→httpdowngrade is refused; - the response body is capped at 10 MB;
timeout,user_agent,cookies,referer, andlanguageare honoured; - TLS certificate verification is enabled by default and is never silently disabled on a
connection error. Passing
verify_ssl=Falseexplicitly is allowed (with a warning) but is vulnerable to man-in-the-middle attacks; - every failure is returned to the model as a
Failed to fetch ...string instead of raising, so the model can adapt.
fs_edit_file is all-or-nothing:
- supported operations and keys:
replace:{"type": "replace", "old": <str>, "new": <str>}— replaces all occurrences.oldmust be non-empty and must differ fromnew;replace_line:{"type": "replace_line", "line_number": <1-based int>, "new": <str>};insert_line:{"type": "insert_line", "line_number": <0..N>, "content": <str>};delete_line:{"type": "delete_line", "line_number": <1-based int>};append:{"type": "append", "content": <str>};- every edit is validated and applied in order; if any edit fails, nothing is written and
the result reports the failures (
success=false,errorexplains how many failed); - writes are atomic and preserve file permissions; a read-only file is rejected with
Permission deniedinstead of being overwritten.
Tool Execution Confirmation¶
By default YAICLI asks for confirmation before running each tool call:
╭─ Confirm tool call ─────────────────────────────────────╮
│ execute_shell_command({"shell_command": "du -sh ."}) │
╰─────────────────────────────────────────────────────────╯
Execute tool? [y]once, [a]session, [A]always, [n]o:
| Key | Meaning |
|---|---|
y |
Allow this call only |
a |
Allow for the rest of this session |
A |
Always allow — saved to tool_permissions.json |
n |
Deny — the model receives a refusal result and can adapt |
Pressing Ctrl-C or sending EOF at the prompt is treated as a denial.
In non-interactive sessions (piped stdin / no TTY), unapproved tools are denied; pre-approve them
with --allowed-tools, --yes (read-level tools only), or persistent rules in
tool_permissions.json. Dangerous shell commands always need an explicit confirmation and are
refused non-interactively.
This section is only a summary. The full model — risk classes, persistent rules
(argv_prefix / path_glob), audit log, and headless approval flags — is documented in
Permissions & Safety; the TOOL_CONFIRM=false escape hatch is described there as
well.
MCP (Model Context Protocol)¶
MCP allows AI models to use external tools:
# Enable MCP in queries
ai 'What is the latest exchange rate between BTC and USD?' --enable-mcp
Configure MCP tools in ~/.config/yaicli/mcp.json.
Image Input (Vision)¶
YAICLI supports sending images to vision-capable models (GPT-5.2, Claude 3, Gemini, Llama 3.2 Vision, etc.) using the --image / -i option.
Local Images¶
Pass a local image file path. The image will be validated and base64-encoded before sending:
ai --image photo.jpg "What is in this image?"
ai -i screenshot.png "Describe the UI layout"
Supported formats: JPEG (.jpg, .jpeg), PNG (.png), GIF (.gif), WebP (.webp).
URL Images¶
Pass an image URL to send it directly to the provider:
ai --image https://example.com/diagram.png "Explain this architecture"
Multiple Images¶
Use -i multiple times to send multiple images in one request:
ai -i before.png -i after.png "What changed between these two screenshots?"
Image-Only Invocation¶
You can send an image without a text prompt:
ai --image photo.jpg
When no text prompt is given, YAICLI sends a minimal prompt (Describe the image.,
or Describe these images. for multiple images) so providers always receive a
valid user message.
Provider Compatibility¶
Image input is automatically formatted for each provider family:
| Provider Family | Providers | Image Format |
|---|---|---|
| OpenAI-compatible | OpenAI, DeepSeek, Groq, Mistral, OpenRouter, etc. (~25) | content array with image_url blocks |
| Anthropic | Anthropic, Anthropic Bedrock, Anthropic Vertex | content array with image + source blocks |
| Gemini | Gemini, Vertex AI | Part.from_bytes() / Part.from_uri() |
| Ollama | Ollama | Separate images array (base64 only) |
Providers without vision support (Cohere, HuggingFace, ChatGLM, ModelScope) will show a warning and send text-only messages.
Limitations¶
- Image input is supported in single-shot mode only, not in interactive
--chatmode - No client-side file size limit; the provider API enforces its own limits
- Ollama may not support URL images; use local files instead
- No automatic image resizing or optimization
Custom Configuration¶
LLM Provider Configuration¶
YAICLI works with many LLM providers. Configure your preferred provider in the config file:
[core]
# Comments must be on their own line; inline comments become part of the value.
PROVIDER=openai
# Leave BASE_URL empty to use the provider default URL.
BASE_URL=
API_KEY=your_api_key_here
MODEL=gpt-5.2
Syntax Highlighting Themes¶
Change the code highlighting theme:
CODE_THEME = monokai
Browse available themes at: https://pygments.org/styles/

Extra Headers and Body Parameters¶
Add custom parameters to API requests:
EXTRA_HEADERS={"X-Extra-Header": "value"}
EXTRA_BODY={"extra_key": "extra_value"}
Example for Qwen3's thinking behavior:
EXTRA_BODY={"enable_thinking": false}
# Or limit thinking tokens:
EXTRA_BODY={"thinking_budget": 4096}