Skip to main content
Documentation menu

Getting started

Compatibility

Chanto picks up what you've already configured for Claude Code and Codex, so a project set up for either works without changes. This compatibility runs one way: Chanto reads their files and leaves them as they are. Run /config to see which files were loaded, and chanto --dump-config to print the merged result.

Claude CodeCodex
Settings~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json: permissions (allow/deny/ask, defaultMode, additionalDirectories), hooks, disableAllHooks, env, statusLine, bashEditDiffEnabled (bash_diff); the env variable CLAUDE_BASH_NO_LOGIN=1 turns off the shell snapshot~/.codex/config.toml, .codex/config.toml: mcp_servers, model_providers, [hooks]; hooks.json beside them; trusted projects
MCP servers.mcp.json, ~/.claude.json (user and per-project, including disabledMcpServers), with ${VAR} / ${VAR:-default} expansion; .mcp.json approval keys enabledMcpjsonServers, disabledMcpjsonServers, enableAllProjectMcpServers; allowedMcpServers/deniedMcpServers; chanto mcp add-json and add-from-claude-desktop[mcp_servers.*] (stdio and HTTP: command, args, env, env_vars, cwd, url, http_headers, env_http_headers, bearer_token_env_var, startup_timeout_sec/_ms, tool_timeout_sec, enabled_tools, disabled_tools, enabled)
Managed settingsmanaged-settings.json and managed-settings.d/*.json (alphabetical) in /Library/Application Support/ClaudeCode/ (macOS), /etc/claude-code/ (Linux, WSL), C:\Program Files\ClaudeCode\ (Windows): permissions (allow/deny/ask, additionalDirectories, defaultMode, disableBypassPermissionsMode), env, allowManagedPermissionRulesOnly, allowedMcpServers/deniedMcpServers, allowManagedMcpServersOnly—
InstructionsCLAUDE.md, .claude/CLAUDE.md, CLAUDE.local.md, ~/.claude/CLAUDE.md, @path imports, .claude/rules/**/*.md (including paths:-scoped rules)AGENTS.md, AGENTS.override.md, ~/.codex/AGENTS.md (Gemini, Cursor, Copilot, Aider and opencode files: below)
Skills, commands, agents.claude/skills, .claude/commands (subfolders namespace them: commands/frontend/test.md is /frontend:test), .claude/agents (subfolders allowed), in home and in every directory from the repository root down to the working directory~/.codex/skills, ~/.codex/prompts (/name or /prompts:name, $1..$9, $NAME), .agents/skills, .codex/skills
Pluginschanto-managed installs and Claude Code plugins enabled in enabledPlugins: commands (/plugin:cmd, or /cmd when unambiguous), agents, skills, hooks (${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}), MCP servers and LSP servers, including components defined in the marketplace entry. /plugins lists them—
Tools—apply_patch patches (as a tool call or apply_patch <<EOF in the shell), shell argv calls, update_plan
CLI-p, -c, -r, --add-dir, --dangerously-skip-permissions, --fallback-model, --permission-mode, --allowed-tools/--disallowed-tools, --output-format, --input-format stream-json, --system-prompt[-file], --append-system-prompt[-file], --mcp-config, --strict-mcp-config, --settings, --setting-sources, --session-id, -n/--name, --no-session-persistence, --json-schema, --agent, --agents (JSON; a number is Chanto's team size), --tools, --plugin-dir, --bare, --permission-prompt-tool, --include-partial-messages (no-op); see Claude Code's flagsexec, --full-auto, --yolo, -s read-only|workspace-write|danger-full-access, -C, --json

How it works:

  • Instruction files are read from your home directory and from every directory between the repository root and the working directory. Files in deeper subdirectories are added to the context the first time the agent reads or edits something there.
  • A skill, command or subagent nearer the working directory replaces one with the same name further up, and project ones replace your home ones.
  • Skills follow Claude Code's format:
    • Every skill also runs as /name [args]. When a skill and a command share a name, the skill wins (built-in commands always win). user-invocable: false hides a skill from /; disable-model-invocation: true hides it from the model and the Skill tool, so only /name runs it.
    • In SKILL.md, $ARGUMENTS is all the arguments, and $ARGUMENTS[N] or $N is one of them, counting from 0 ($0 is the first; command files keep $1 as the first). ${CLAUDE_SKILL_DIR} is the skill's directory. Arguments that no placeholder takes are appended as ARGUMENTS: <value>. !`cmd` at the start of a line or after a space is replaced by the command's output before the model sees it, in skills and commands alike; project skills and commands don't run them in a folder you haven't trusted. Arguments are filled in after the blocks run, so they can't add a block or a command. Inside a block they are data: all of them are in the ARGUMENTS environment variable ("$ARGUMENTS", or $env:ARGUMENTS in PowerShell); in a command file's blocks run by a POSIX shell, the words are also "$1".."$9", and a NAME=value argument is $NAME when the block uses it and the environment has no NAME already. Quote them, as in !`git log "$1"`.
    • context: fork runs the skill in a subagent (agent: picks the type, default general-purpose). It runs in the background and its report arrives when it finishes; background: false waits for it in the turn that invoked the skill, and so do non-interactive runs (-p).
    • Skills and command files also read argument-hint (shown next to /name in completion and /help), allowed-tools (permission rules, space- or comma-separated or a list, that are pre-approved during the turn that invokes it; deny and ask rules still win, and the grant clears with your next message), model and effort (for the rest of that turn; with context: fork, for the subagent), shell (bash or powershell for its !`cmd` blocks) and hooks (registered when it's invoked and kept for the rest of the session; once: true removes a hook after its first successful run; a project skill's hooks need folder trust).
    • Boolean fields accept true/false, yes/no, on/off and 1/0 in any case.
  • Subagent files read Claude Code's frontmatter; unknown keys are ignored:
    • tools and disallowedTools are a comma-separated string or a YAML list. A disallowedTools entry like Bash(git push *) removes the whole tool. If Chanto can't read either value, the subagent gets no tools rather than all of them.
    • model: inherit uses the main conversation's model. sonnet, opus, haiku and fable become the newest Claude model of that family when the subagent's provider is the Anthropic API (anthropic/… or a kind = "anthropic" provider). On other providers, including Bedrock, Vertex AI and Foundry, the subagent keeps its usual model and Chanto warns once. Any other value is a model spec.
    • permissionMode (default/manual, acceptEdits, auto, dontAsk, plan, bypassPermissions) applies while the main conversation is in default or plan mode; in bypass, acceptEdits or auto mode the subagent runs in that mode, and it never gets bypass on its own. dontAsk denies anything that would prompt.
    • maxTurns caps its model calls; at the limit its last message comes back marked partial, and it can be resumed.
    • skills preloads up to 32 skills' full content into its prompt (skills with disable-model-invocation can't be preloaded; missing ones are skipped).
    • mcpServers lists configured server names (kept reachable through a tools list) and inline definitions (- name: {type, command, args, url, ...}), which connect when the subagent starts and close when it's done. A project agent's inline servers and its hooks need folder trust.
    • hooks (same layout as settings) run only while the subagent does; Stop hooks fire as SubagentStop.
    • memory: user|project|local gives it a directory (~/.claude/agent-memory/<name>/, .claude/agent-memory/<name>/, .claude/agent-memory-local/<name>/) with instructions and the first 200 lines of its MEMORY.md in its prompt, and adds Read, Write and Edit to its tools. Off when [memory] auto = false.
    • background: true always runs it in the background, isolation: worktree in its own git worktree, effort sets its effort level, color colors its name in the result line, and omitClaudeMd: true leaves the instruction files out of its prompt.
    • With --agent <name> the definition runs as the main session: its tools, model, prompt, permissionMode, maxTurns, effort, skills, mcpServers, hooks and memory apply, and initialPrompt becomes the first message of a new session (ahead of any prompt you pass). --agents '<json>' takes the same fields under the same keys.
  • Frontmatter is read as YAML: quoted strings, > and | blocks, lists, nested maps (hooks:, mcpServers:), flow [...]/{...} collections and comments.
  • Permission rules follow Claude Code's rules:
    • Edit(...) covers every tool that writes files and Read(...) every tool that reads them.
    • In paths, // means absolute, ~/ means home, a leading / is relative to the project and ./ is relative to the working directory.
    • mcp__server matches all of that server's tools.
    • A bare Bash (or Bash(*)) allow rule covers every command, including ones with $(…), backticks or <(…). A prefix rule such as Bash(git:*) doesn't cover those, since the substituted command isn't visible in the text: git $(rm -rf ~) still needs approval. Deny and ask rules win over either.
    • When a non-interactive run (-p) refuses a call that needs approval, the message suggests the allow rule that "don't ask again" would add for it, such as Bash(npm run test:*) or Bash(git push:*).
  • Imported MCP servers that need an OAuth login show as "needs login" (/mcp login <name>); ones that fail to connect for other reasons are skipped with a single warning.
  • Project-level files go through the same folder trust check as .chanto/config.toml. If you already trusted a folder in Claude Code or Codex, Chanto trusts it too.
  • To turn imports off, set compat = { claude = false, codex = false } in ~/.chanto/config.toml.
  • Claude Code managed settings (your organization's policy) are read whatever compat says and applied above every other layer, profiles included: their deny and ask rules can't be removed by any other file, and their env wins. With allowManagedPermissionRulesOnly: true, only the policy's own allow rules count (deny and ask rules from your files still apply). permissions.defaultMode sets the mode every session starts in, over your config and command-line flags (you can still switch modes during a session). permissions.disableBypassPermissionsMode: "disable" rules out bypass mode entirely: --dangerously-skip-permissions, a config mode, /mode bypass, ACP and the HTTP API all fall back to default mode with a warning. Other managed keys are ignored. A managed file that isn't a JSON object stops Chanto from starting, as it does Claude Code; chanto doctor lists the managed files among the config layers. Debug builds accept CHANTO_MANAGED_SETTINGS_DIR to test a policy from another directory; release builds always read the real one.
  • MCP_TOOL_TIMEOUT (milliseconds) limits MCP tool calls as in Claude Code, for servers without tool_timeout_sec when mcp_tool_timeout isn't set; unset, the limit is 10 minutes.
  • MCP tool search follows Claude Code's ToolSearch: the same query forms, ENABLE_TOOL_SEARCH values (true, false, auto, auto:N), alwaysLoad server setting and anthropic/alwaysLoad tool metadata. Two differences: unset means auto (Claude Code always defers on its own API), since small local models do better with tools in view while the list is small; and loaded definitions are sent as ordinary tools appended to the list (and in the ToolSearch result) rather than as tool_reference blocks, which only Anthropic's API supports. See MCP servers.
  • --fallback-model and fallback_model work like Claude Code's --fallback-model and fallbackModel (a chain of up to three, tried in order, for the current turn only, with a notice when it switches). Chanto also switches when a local model server refuses the connection, which Claude Code treats as a transport error. Claude Code's fallbackModel setting isn't imported, since its model names don't apply to other providers. See Fallback models.
  • compat.codex_rules = true also imports the command approvals in ~/.codex/rules/*.rules (prefix_rule) as Bash allow, ask and deny rules.

Other tools' instruction files

Chanto also reads the instruction files other coding tools keep, in each directory from the repository root down to the working directory:

ToolFilesSwitch
Gemini CLI~/.gemini/GEMINI.md, GEMINI.md (hierarchical: subdirectory files load once the agent works there, like AGENTS.md)compat.gemini
Cursor.cursor/rules/**/*.mdc (and .md): alwaysApply: true always loads; globs loads the rule when the agent touches a matching file; a rule with only a description is listed for the model to read when relevant; manual rules aren't loaded. Legacy .cursorrules always loadscompat.cursor
GitHub Copilot.github/copilot-instructions.md; .github/instructions/**/*.instructions.md by applyTo (** = always, other globs = when a matching file is touched)compat.copilot
Aiderthe files .aider.conf.yml lists under read: (e.g. CONVENTIONS.md); nothing elsecompat.aider
opencode~/.config/opencode/AGENTS.md; instructions paths and globs in opencode.json; subagents in .opencode/agent/*.md and ~/.config/opencode/agent/ (mode: primary agents are skipped; tools: { write: false } entries become disallowed tools)compat.opencode

Project files from these tools load only in a trusted folder; user-level files always load. Paths they reference must stay inside the project. Files with identical content (an AGENTS.md symlinked as GEMINI.md, the same text in two tools' files) load once. All are on by default; turn one off in ~/.chanto/config.toml, e.g. compat = { cursor = false }. /config lists the instruction files loaded and the path-scoped rules waiting for a match.

Hooks

Hooks follow Claude Code's protocol for its events and handler types. Hooks written for Claude Code or Codex work unchanged.

  • Events: all of Claude Code's events are accepted. Each fires at the matching moment in Chanto, with Claude Code's input fields, matcher and decision handling:

    • PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, UserPromptSubmit, Stop, SubagentStart, SubagentStop, SessionStart, SessionEnd: as in Claude Code. Notification fires for permission prompts and, with notification_type: "idle_prompt", a minute after a turn ends while you seem to be away (nothing typed, nothing running in the background; the TUI also needs the terminal unfocused).
    • PreCompact / PostCompact: around /compact (manual) and automatic compaction (auto). Exit 2 or decision: "block" on PreCompact skips the compaction. PostCompact gets compact_summary.
    • PermissionDenied: auto mode refused a tool call (the classifier or a dangerous-command check). hookSpecificOutput.retry: true tells the model it may retry, except when the classifier was unavailable or timed out.
    • PostToolBatch: after every call of a batch has its result, before the next model call. tool_calls lists each call's tool_name, tool_input, tool_use_id and tool_response (the text the model receives). additionalContext reaches the model; decision: "block" or continue: false ends the turn.
    • StopFailure: the turn ended on a model request error. error is Claude Code's error type, guessed from the provider's error text (rate_limit, authentication_failed, billing_error, model_not_found, server_error, ...), plus error_details and last_assistant_message. Output is ignored.
    • MessageDisplay: the assistant's text passes through the hook before it's shown, in batches of whole lines (the whole message at once in -p and other non-interactive runs). hookSpecificOutput.displayContent replaces what's displayed; the conversation keeps the original. Default timeout 10 seconds.
    • UserPromptExpansion: a typed /command expands into a prompt: custom commands and skills (expansion_type: "slash_command", command_source project or user) and MCP prompts (mcp_prompt). Exit 2 or decision: "block" stops it; additionalContext goes along with the prompt. Built-in commands such as /review don't fire it.
    • TaskCreated / TaskCompleted: Chanto's tasks are the TodoWrite list. A new item is a task being created; an item turning completed is a task being completed. task_id is the item's position, task_subject its text. A block keeps that item out of the list, or keeps it from being marked completed, and the hook's message goes back to the model as the tool's error.
    • TeammateIdle: a /team sub-task's run finished, before the lead reviews it (teammate_name is the sub-task id, team_name the team id). Exit 2 runs the sub-task again with the hook's message, told to pick up its earlier branch first (up to five runs per sub-task); continue: false stops it.
    • InstructionsLoaded: instruction files (AGENTS.md, CLAUDE.md, CHANTO.md, .claude/rules/*.md) loaded at session start (load_reason: "session_start", compact after a compaction) and nested ones loaded when the agent touches a subdirectory (nested_traversal, or path_glob_match for rules with paths:). Unlike Claude Code it also fires for AGENTS.md, Chanto's own instruction file. Runs in the background.
    • ConfigChange: Chanto applies changed settings when you run /reload, so that's when it fires, once per settings file changed since they were last loaded (user_settings, project_settings, local_settings, policy_settings, skills; Chanto's config.toml files count with the matching Claude settings). A block keeps the current settings, except for policy_settings. Unlike Claude Code, /reload says it was blocked.
    • CwdChanged: a Bash command in the main conversation changed the session's working directory (old_cwd, new_cwd). watchPaths sets the files FileChanged watches. Its hooks get CLAUDE_ENV_FILE (see below); what they write there lasts until the next CwdChanged.
    • FileChanged: the files named in the FileChanged matchers (in the working directory) and any watchPaths a SessionStart, CwdChanged or FileChanged hook returned are polled twice a second. A change, creation or deletion fires the hooks whose matcher fits the file name, with file_path and event (change, add, unlink). Its hooks get the same CLAUDE_ENV_FILE as CwdChanged hooks.
    • DirectoryAdded: /add-dir added a directory that wasn't already in the workspace (source: "slash_command"). Runs in the background (600-second default timeout); a systemMessage reaches the model with the next prompt. --add-dir doesn't fire it, as in Claude Code.
    • WorktreeCreate / WorktreeRemove: a WorktreeCreate hook replaces git for --worktree sessions and for subagents started with isolation: "worktree"; it gets name and prints the directory's path as the last line of stdout (an http hook returns hookSpecificOutput.worktreePath). A failure, or no usable directory, fails the creation. On leaving a --worktree session whose worktree a hook made, Chanto removes it when git sees no changes in it: through the WorktreeRemove hooks (worktree_path; a non-zero exit fails the removal if the directory is still there), or without one, as git worktree remove --force would. Subagents' hook-made worktrees are kept, as in Claude Code.
    • PreModelSwitch / PostModelSwitch: /model <name> (source: "command") and an ACP client's model change (sdk). The matcher sees the model name without its provider/ prefix. PreModelSwitch can refuse the switch (exit 2, decision: "block", permissionDecision: "deny", or not answering within its 30-second timeout) or ask (ask: /model asks you to confirm; elsewhere it refuses). PostModelSwitch also fires when resuming a session restores its model (resume); its stdout or additionalContext reaches the model with the next prompt. context_tokens is an estimate, prompt_cache_warm means a model reply arrived in the last five minutes, and estimated_cache_write_usd uses Chanto's price list (0 with pricing: "default" for models it can't price).
    • Elicitation / ElicitationResult: around an MCP server's elicitation/create, matched on the server name. An Elicitation hook can answer for you (hookSpecificOutput.action accept with content, decline or cancel); exit 2 or decision: "block" declines. Otherwise you answer, and ElicitationResult hooks can change or block your answer the same way. Hooks also run in -p mode, where nobody is there to answer and requests are otherwise declined.
    • Setup: fires only for chanto --init-only (runs the Setup hooks with trigger init, then the SessionStart hooks, and exits without a conversation) and for chanto -p --init / -p --maintenance (trigger init / maintenance, before the prompt runs; without -p these flags are an error). The matcher and the trigger input field are the trigger. Only command hooks run; Setup can't block, any exit code continues, and its JSON output is discarded.

    Codex's Interrupt isn't fired. Hooks configured for unknown events produce a startup warning.

  • Handler types: all of Claude Code's: command, http, mcp_tool, prompt and agent, on the events Claude Code allows them (others are skipped with a single warning). A handler of an unknown type, or without the fields its type needs, is reported at startup and by chanto doctor.

    • command: args switches to exec form (command is spawned directly with args, no shell; ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_DATA} are substituted). shell: "powershell" runs it with PowerShell (pwsh where installed outside Windows). async: true runs it in the background without blocking or a timeout; when it exits, its additionalContext and systemMessage reach the model on its next turn. asyncRewake: true does the same within its timeout, and exit code 2 also wakes an idle session with the hook's stderr (or stdout).
    • mcp_tool: server, tool and input, whose strings take ${path} values from the hook input ("${tool_input.file_path}"). The tool's text is read like a command's stdout; an error result, or a server that's missing or not connected, is a non-blocking error. Not run for Setup or SessionStart at launch or resume.
    • prompt: one model call (the hook's model, or the session's) with the prompt, where $ARGUMENTS is the hook input JSON (appended when absent; \$ is a literal $). The model answers {"ok": true} or {"ok": false, "reason": "..."}. ok: false keeps a Stop/SubagentStop agent working (unless it also says "impossible": true), ends the turn on PreToolUse and PostToolUse with the reason as a warning (continueOnBlock: true instead returns it to the model: a denied tool call, or feedback), blocks UserPromptSubmit, UserPromptExpansion, PostToolBatch, PostToolUseFailure, TaskCreated and TaskCompleted, and does nothing on PermissionRequest and PermissionDenied. Default timeout 30 seconds.
    • agent: like prompt, but a read-only subagent (Read, Grep, Glob, Ls; up to 50 model calls, no hooks of its own) investigates before answering, and ok: false acts as continueOnBlock: true. Not on PermissionRequest. Default timeout 60 seconds.
  • onFailure: "block" (command and http hooks): a hook that can't start, exits with a code other than 0 or 2, times out or prints invalid JSON does what exit 2 does on that event (denies on PermissionRequest), with failed; blocking because onFailure is "block" (or timed out) in the message. No effect on Stop, SubagentStop, TaskCompleted, TeammateIdle and background hooks.

  • disableAllHooks: true (or disable_all_hooks = true) turns all hooks off, skill and subagent hooks included. A project setting needs folder trust.

  • Hooks in skills and subagents: see their frontmatter above. once: true is honored for skill hooks only.

  • Matchers: empty or * matches everything. A value made only of letters, digits, _, -, spaces, , and | is a list of exact names (Edit|Write or Edit, Write). Anything else is an unanchored regex (^Notebook, mcp__memory__.*). Matchers that name Edit, Write or MultiEdit also match apply_patch.

  • if: a handler can carry one permission rule, such as "if": "Bash(git *)" or "if": "Edit(**/*.ts)". The hook runs only when the tool call matches that rule, using the same matcher as permission rules. For Bash, every subcommand is checked, including commands inside $(...) and backticks, with leading VAR=value assignments ignored. When Chanto can't tell what a command runs (nested substitutions), the hook runs, as in Claude Code. if applies to tool events only. A hook whose if is malformed or set on another event is skipped, with a single warning.

  • Input: a command hook gets JSON on stdin with hook_event_name, session_id, transcript_path, cwd and permission_mode, plus the event's own fields:

    • Tool events: tool_name and tool_input.
    • PostToolUse: a tool_response object. Bash's is stdout, stderr, interrupted and isImage, with stdout and stderr interleaved in stdout. Write's is type, filePath and content. Edit's is filePath, oldString, newString and replaceAll. Read's is type and file.filePath/file.content. Other tools get {"output": ...}. The plain text the model saw is also sent as tool_output.
    • PostToolUseFailure: error and is_interrupt.
    • Stop/SubagentStop: last_assistant_message (also sent as last_message for older hooks) and stop_hook_active.

    The environment has CLAUDE_PROJECT_DIR and CHANTO_PROJECT_DIR. Payload fields Claude Code sends that Chanto doesn't include: tool_use_id (sent on PermissionDenied and PostToolBatch only), duration_ms, agent_id, background_tasks, prompt_id, effort.

  • Persisted environment (CLAUDE_ENV_FILE): SessionStart, Setup, CwdChanged and FileChanged command hooks get CLAUDE_ENV_FILE (also as CHANTO_ENV_FILE), a per-session file under ~/.chanto/session-env/ that's kept when you resume. Other events don't get it. Append export NAME=value lines to it, and every later Bash command in the session (background ones and subagents' too) runs the file first, so a line can use anything Bash evaluates, such as export PATH="$PATH:./node_modules/.bin". What CwdChanged and FileChanged hooks write lasts until the next CwdChanged. A CLAUDE_ENV_FILE set before Chanto starts is run before each command as well. Sandboxed commands can read the files. Commands in a background run's container don't get them. Where the shell is PowerShell or cmd (Windows without Git Bash, or CHANTO_SHELL), nothing runs the files and hooks don't get CLAUDE_ENV_FILE, as in Claude Code. Variables from a launch-time CLAUDE_ENV_FILE are still copied into the command's environment if every line of the files is blank, a # comment or a plain assignment (export NAME=value, declare -x NAME=value or NAME=value, with unquoted [A-Za-z0-9_@%+=:,./-] text, single-quoted text, or double-quoted text with $, backticks and " escaped). If any line is something else ($PATH, source, $'...'), none are copied and the debug log says Session environment is not all plain assignments. On Windows, values containing / or \ are left out, and sandboxed commands get none.

  • HTTP hooks: {"type": "http", "url": "...", "headers": {...}, "allowedEnvVars": [...], "timeout": 30} POSTs the same JSON payload. $VAR/${VAR} in headers expand only for variables listed in allowedEnvVars; unlisted ones become empty. A 2xx response with an empty body counts as success. A 2xx response with a JSON object body is read like a command hook's stdout. Any other body, a non-2xx status or a connection error is a non-blocking error (or a block with onFailure: "block"), and a timeout cancels the hook. Like command hooks, http hooks don't run on SessionStart and Setup.

  • Blocking: exit code 2 blocks, and stderr (or stdout, if stderr is empty) is passed back to the model. A hook that blocks or denies without a reason is named instead: hook [<command>] exited with code 2 and gave no reason.

  • JSON output: a hook can print JSON on stdout to approve, deny or ask (hookSpecificOutput.permissionDecision, or decision.behavior for PermissionRequest). It can also rewrite the tool input (updatedInput), add context (additionalContext), make the agent keep going (decision: "block" on Stop), stop the turn (continue: false) or show a message (systemMessage). On PostToolUse, updatedToolOutput replaces what the model sees when it's shaped like the tool's tool_response (Bash's stdout/stderr, Read's file.content, other tools' output); updatedMCPToolOutput does the same for MCP tools only, which also accept text or content blocks. The transcript display keeps the original. suppressOutput is ignored.

  • Timeouts: 60 seconds by default for command, http and mcp_tool hooks (Claude Code's default is longer), except MessageDisplay (10), PreModelSwitch (30) and DirectoryAdded (600); prompt 30 and agent 60. Hooks for one event run one after another, not in parallel.

  • Config: hooks go in TOML or JSON. In TOML, use [[hooks.PreToolUse]] with matcher, command or type = "http" plus url, and optional if and timeout_secs. In JSON, use Claude's layout in .claude/settings.json. Codex hooks are also read: ~/.codex/hooks.json and [hooks] in ~/.codex/config.toml, plus .codex/hooks.json and .codex/config.toml in the project. Codex hooks are not read when compat.codex is off or Codex's [features] hooks = false (or codex_hooks = false) is set. Project hooks from any source need folder trust. Chanto doesn't check Codex's per-hook review state. /hooks lists configured hooks.

  • Plugins: plugin hooks and stdio MCP servers get CLAUDE_PLUGIN_ROOT (the plugin directory) and CLAUDE_PLUGIN_DATA in their environment. CLAUDE_PLUGIN_DATA is a per-plugin directory, ~/.chanto/plugin-data/<plugin>, created when first needed. ${CLAUDE_PLUGIN_ROOT} and ${CLAUDE_PLUGIN_DATA} are substituted in their definitions.

Format and lint after edits

After Write, Edit, MultiEdit or apply_patch succeeds, Chanto runs each changed file's formatter (in place), then its linter, from the project root. The model is told what the formatter changed, as a small diff (or "Read it again" for large rewrites), so its view of the file stays right, and the formatter's write doesn't count as an outside change for the next edit. A linter that exits non-zero sends its output to the model as <lint file="..." command="...">; the transcript shows a one-line summary. A formatter that fails leaves the file as written and says why.

toml
[format]
auto = true                    # use the project's own tools (off by default)
timeout_secs = 15
[format.commands]              # glob or extension = command; {file} is the path (else appended)
"*.rs" = "rustfmt --edition 2021"
"web/**/*.{ts,tsx}" = "npx prettier --write {file}"

[lint]
auto = true                    # ruff check (when ruff is configured), eslint (when in package.json)
timeout_secs = 60
[lint.commands]
py = "ruff check --quiet"

Explicit commands are always used; the most specific pattern wins, and enabled = false turns the section off. With auto = true, Chanto picks a tool only when the project clearly uses it: rustfmt --edition <Cargo.toml's> for Rust with rustfmt installed, gofmt -w with a go.mod, ruff format when ruff is configured (ruff.toml or [tool.ruff]), else black when [tool.black] is, clang-format -i with a .clang-format, and prettier for JS/TS/CSS/JSON/Markdown/HTML/YAML when it's in package.json. Only files inside the project are touched. These are your commands, run like hooks: [format] and [lint] in a project config apply only once the folder is trusted, nothing runs under the read-only sandbox policy or when an editor (ACP) owns the file, and a background run's commands run in its container. They complement the done-gate (--verify), which still runs the project's checks at the end.

The path that {file} stands for is quoted for the shell that runs the command (single quotes for bash and PowerShell; under cmd.exe it becomes "%CHANTO_FILE%"), so $(…), backticks or %VAR% in a file name are never expanded. The path is also in the CHANTO_FILE environment variable. A format, lint or verify command is finished when its own process exits: output arriving within a short grace period after that is kept, and a daemon it left running in the background (holding the output open) no longer stalls the turn until the time limit.

Language servers

Servers start lazily (a file of that language is read or edited), one per language per project root (nearest Cargo.toml, package.json/tsconfig.json, pyproject.toml, go.mod, compile_commands.json, …). After Write/Edit/MultiEdit, Chanto waits briefly for diagnostics (2.5 s by default, 8 s for rust-analyzer while it indexes; usually well under a second once warm) and reports errors only, as

<diagnostics file="src/main.rs">
L4:10 error: cannot move out of `v` because it is borrowed [rustc E0505]
</diagnostics>
toml
[lsp]
# enabled = false              # turn off language servers
# diagnostics_wait_ms = 2500
[lsp.servers.zls]              # add a server (or override a built-in by name)
command = "zls"
extensions = ["zig"]

Some servers run project code: rust-analyzer runs build scripts and proc macros, typescript-language-server loads the project's own TypeScript, gopls may download toolchains, and pylsp loads linter plugins. These only start in trusted folders. pyright and clangd run anywhere. Servers only start for files inside the project or an --add-dir directory, with at most 4 running at once. In a Cargo workspace, one rust-analyzer serves all member crates. [lsp] in a project config is ignored until the folder is trusted. Set CHANTO_LSP_DEBUG=1 to log server traffic to stderr.

Environment overrides: CHANTO_MODEL, CHANTO_BASE_URL, CHANTO_PROVIDER, CHANTO_API_KEY, CHANTO_HOME, BRAVE_API_KEY, TAVILY_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY.

Hosted models

kindAPIDefault base_urlKey
ollamaOllama /api/chathttp://localhost:11434—
openaiOpenAI-compatible /chat/completions (llama.cpp, LM Studio, vLLM, …)http://localhost:8080/v1api_key
anthropicAnthropic Messages APIhttps://api.anthropic.comapi_key or ANTHROPIC_API_KEY
openai-responsesOpenAI Responses APIhttps://api.openai.com/v1api_key or OPENAI_API_KEY
bedrock, vertex, azure, foundryCloud platforms, see Cloud providersfrom region, project or resourcethe cloud's credentials

A [providers.<name>] section without kind is ollama, unless its base_url ends in /v1: then it's openai. If the server answers in the other format (Server-Sent Events or choices to the Ollama provider, Ollama's NDJSON to the OpenAI one), the error says so and names the provider/kind to set.

Built-in providers need no [providers] section: chanto providers lists them (Anthropic, OpenAI, your ChatGPT plan, OpenRouter, Gemini, DeepSeek, xAI, Mistral, Groq, Cerebras, Together, Fireworks, Moonshot, Z.ai, MiniMax, DashScope, Hugging Face, NVIDIA, Vercel AI Gateway, OpenCode Zen, Baseten, Perplexity, Cohere, DeepInfra, Nebius, SambaNova, SiliconFlow, Upstage, AI21, Inception, Requesty, Venice, Chutes, Nano-GPT, Scaleway, DigitalOcean, Weights & Biases, OVHcloud, Crusoe, Morph, Poe, StepFun, Volcengine (Doubao), Xiaomi MiMo, Ollama Cloud, the coding plans, the cloud platforms and Chanto cloud) and whether you're logged in. Each works with its key variable (GEMINI_API_KEY, GROQ_API_KEY, …) or chanto login <provider>. With ANTHROPIC_API_KEY or OPENAI_API_KEY set, the provider names anthropic and openai work without any config. For example, chanto -m anthropic/claude-opus-5-5, subagent_model = "anthropic/claude-haiku-5-5" or /model openai/gpt-6.1-sol. To use one as the default provider, set provider = "anthropic" and model = "claude-opus-5-5".

For a ChatGPT plan, run chanto login chatgpt (or /login chatgpt), then /models to discover your account's available model names. Select an exact name with /model chatgpt/<model> or chanto -m chatgpt/<model>. Logging in does not select a model, and Chanto stores its credentials separately from Codex. Model access depends on your account; a model-access rejection is different from an expired or rejected login. OpenAI API-key access remains separate as openai/<model>. The chatgpt login uses Sign in with ChatGPT and draws from your existing Codex/ChatGPT Work allowance; a separate login to the same account does not add quota. Review connected-app usage and limits in ChatGPT Settings → Usage. See OpenAI’s explanation.

Subscription coding plans

Subscription coding plans whose vendors allow them in third-party coding tools are built in. Each takes the plan's own key, which is not the vendor's pay-as-you-go API key, through chanto login <provider> (or --as <name> for a second account) or its variable. Most speak the vendor's OpenAI-compatible endpoint (MiniMax's M Plan is Anthropic-compatible; see below), and their usage shows no per-token price. Anthropic's Claude Code subscription (Pro/Max) is intentionally absent: its terms don't permit use from other tools, so use the anthropic provider with an API key or Chanto cloud instead.

sh
chanto login kimi-coding   # paste a Kimi Code membership key
chanto login zai           # paste a Z.ai GLM Coding Plan key

Inside a session, the equivalents are /login kimi-coding and /login zai. Login saves credentials; select a model separately with /model kimi-coding/kimi-for-coding, or use /models after choosing the provider to discover its models. These logins use plan keys rather than a browser sign-in.

ProviderPlanEndpointKeyModels
zaiZ.ai GLM Coding Planhttps://api.z.ai/api/coding/paas/v4ZAI_API_KEY/models
kimi-codingKimi Code membershiphttps://api.kimi.ai/coding/v1KIMI_CODE_API_KEY; create one in the Kimi Code consolekimi-for-coding (1M context), kimi-for-coding-highspeed, k3, k3-256k
kimi-coding-cnKimi Code membership (China)https://api.kimi.com/coding/v1KIMI_CODE_API_KEYsame as kimi-coding
minimax-codingMiniMax M Planhttps://api.minimax.io/v1MINIMAX_CODING_API_KEY; the sk-cp-… subscription key from the M Plan consoleMiniMax-M3.1-Flash-Preview (1M context), MiniMax-M3, MiniMax-M2.7
qwen-codingAlibaba Qwen Code planhttps://coding-intl.dashscope.aliyuncs.com/v1ALIBABA_CODING_PLAN_API_KEY/models
qwen-coding-cnAlibaba Qwen Code plan (China)https://coding.dashscope.aliyuncs.com/v1ALIBABA_CODING_PLAN_API_KEY/models
tencent-codingTencent CodeBuddy planhttps://api.lkeap.cloud.tencent.com/coding/v3TENCENT_CODING_PLAN_API_KEY/models
volcengine-codingVolcengine (Doubao) coding planhttps://ark.cn-beijing.volces.com/api/coding/v3ARK_CODING_PLAN_API_KEY/models
stepfun-planStepFun Step planhttps://api.stepfun.ai/step_plan/v1STEPFUN_API_KEY/models
stepfun-plan-cnStepFun Step plan (China)https://api.stepfun.com/step_plan/v1STEPFUN_API_KEY/models
clineCline Passhttps://api.cline.bot/api/v1CLINE_API_KEY/models

For example, chanto login kimi-coding, then chanto -m kimi-coding/kimi-for-coding. /models lists what the plan's API reports, or the vendor-documented models above if it has no list. Kimi's k3 reaches a 1M window only on higher membership tiers, so Chanto budgets 256K for it. The -cn providers use the mainland-China endpoint with the same key, so pick whichever matches where your account was created. MiniMax's M Plan is Anthropic-compatible, so point [providers.minimax-coding] at https://api.minimax.cn/anthropic/v1 (China) or https://api.minimax.io/anthropic/v1 with kind = "anthropic" if your key only works there.

Usage and provider errors

/usage (alias /cost) reports session tokens, spend and any account limits reported by the provider. The footer shows the most-used reported window, its remaining percentage, a local reset time when available, and /usage when space permits:

text
5h limit: 27% left · resets at 10:09 PM · /usage

On a narrow terminal, reset details and the hint drop before the percentage. At 25% remaining, the quota turns amber and a one-time heads-up appears; at 10% remaining, it turns red and warns once more. Warnings re-arm after a reset. Weekly limits can take precedence when they are more exhausted than the 5-hour window. /usage lists all reported windows.

These are the provider’s last reported values, read from response headers or stream events; Chanto does not poll a separate quota endpoint or estimate missing percentages. If no limits have been reported, /usage says so. An elapsed reset timestamp displays reset due until a fresh report arrives; it does not imply that the allowance has already replenished. The separate ctx percentage is conversation-context usage, not your plan’s remaining allowance. Quota banners are for the user and are not automatically added to the model prompt. Limits and resets are enforced by the provider and depend on your plan.

Provider safety notices. Refusal text and links appear in the streamed reply and transcript when the provider supplies them. A safety-filter stop without an explanation shows a clear fallback notice. Filtered turns end without automatic retries, tool execution or continuation through verification/stop hooks. Provider-specific access-program wording is shown only when supplied; Chanto does not invent it.

Waiting out a usage limit. When a plan's 5-hour or weekly window runs out mid-turn (usage_limit_reached, with a reset time from the error, Retry-After, or the last reported limits), Chanto can keep the session and continue the same turn when the window resets instead of failing it. With resume_after_limit = true it always waits; when unset, the TUI asks "Wait and continue at 14:05?" (a reset on another day shows the day), and -p runs fail as before. While waiting, the status line shows ⏸ usage limit · resuming at 14:05 and the spinner counts down; esc stops waiting and ends the turn. When it resumes, Chanto sends a desktop notification (the notifications setting) and fires a Notification hook with notification_type: "limit_resumed". Billing quotas (insufficient_quota) don't reset on a clock, so they still fail right away, and a reset more than eight days off isn't waited for.

toml
resume_after_limit = true   # false: never wait; unset: ask in the TUI

Several accounts per provider. chanto login <provider> --as <name> saves another login for the same provider (a work and a personal ChatGPT plan, two API keys) beside the default one; --with-api-key and the browser logins take --as too, and chanto logout <provider> --as <name> removes one. account = "<name>" in the config (or a profile) uses that login for every provider that has one by that name, and the default login for the rest. In a session, /login lists saved logins with the one in use marked, /login <provider> <name> switches for the rest of the session (default goes back to the unnamed one), /login chatgpt --as <name> signs in as a new account and switches to it, and /status shows the account in use. Named logins are stored in ~/.chanto/auth.json as <provider>@<name> entries, so older Chanto versions keep reading the default ones. Listings never show keys or tokens. A project config can set account only once the folder is trusted.

Hosted provider behavior

How the hosted providers behave:

  • Anthropic:
    • Prompt caching is on, with breakpoints on the system prompt, the tool list and the latest user turn.
    • Thinking is adaptive on current models and uses a thinking budget on older ones.
    • Thinking blocks and their signatures are sent back unchanged with the turn they belong to.
  • OpenAI Responses:
    • Requests are stateless (store: false).
    • Reasoning models get reasoning.effort and return encrypted reasoning, which is passed back with the following turns.
  • Both:
    • Rate-limit and overload errors (429, 529, 5xx) are retried, honoring retry-after.
    • The context budget defaults to the model's window (from Anthropic's Models API, or a per-model default for OpenAI) instead of num_ctx.

Cloud providers (Bedrock, Vertex AI, Azure)

Organizations that must use their cloud's model endpoints can select them like any other provider:

An AWS profile or gcloud login on your machine is not taken as a choice to use it: /model and chanto models list a cloud platform's models only once you opt in, with a key saved by chanto login <platform>, the platform's API-key variable (AWS_BEARER_TOKEN_BEDROCK, …), a [providers.<platform>] section, a default model on that platform, or CHANTO_USE_BEDROCK=1 / CHANTO_USE_VERTEX=1 / CHANTO_USE_AZURE=1 / CHANTO_USE_FOUNDRY=1 (Claude Code's CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX and CLAUDE_CODE_USE_FOUNDRY count too). chanto setup still offers them when it finds credentials, and chanto providers shows what it found.

SpecPlatformWire APICredentials
bedrock/<model-id or inference profile>Amazon BedrockClaude: InvokeModelWithResponseStream (Anthropic Messages). Others: ConverseStreamAWS credential chain (SigV4), or a Bedrock API key
vertex/<model>Google Vertex AIClaude: streamRawPredict (Anthropic Messages). Gemini and Model Garden: OpenAI-compatible endpointApplication Default Credentials
azure/<deployment>Azure OpenAI (v1 API)chat completions, or Responses for codex and -pro deploymentsAPI key or Microsoft Entra ID
foundry/<model>Azure AI FoundryClaude: Anthropic Messages. Others: OpenAI-compatible chat completionsAPI key or Microsoft Entra ID

Credentials are resolved on the first request to a cloud model, never at startup, and cached until five minutes before they expire. Claude on every cloud gets the same prompt caching, thinking and tool handling as the Anthropic API. The context budget for Claude on a cloud is 200k tokens. chanto doctor shows where credentials come from (never their values), the region, project or endpoint, and the result of a check that spends no tokens. chanto providers shows the same in one line per cloud. Setup suggests a cloud only when credentials and a region, project or endpoint are present, and ranks it after local models.

Optional settings, in ~/.chanto/config.toml. A section named after a platform needs no kind. A section with another name needs a kind (kind = "bedrock", for example) and can point at a separate account:

toml
[providers.bedrock]
region = "us-east-1"        # default: AWS_REGION, AWS_DEFAULT_REGION, then the profile's region
profile = "ml-dev"          # default: AWS_PROFILE, then "default". If set, it wins over AWS_* key variables
# api = "converse"          # force the Converse API (default: Anthropic Messages for Claude model IDs and ARNs that name one)
# base_url = "https://vpce-….bedrock-runtime.us-east-1.vpce.amazonaws.com"   # PrivateLink endpoint

[providers.vertex]
project = "my-project"      # default: GOOGLE_CLOUD_PROJECT, then the service account's or gcloud's project, then the ADC quota project
region = "us-east5"         # default: CLOUD_ML_REGION, then "global"

[providers.azure]
resource = "my-openai"      # https://my-openai.openai.azure.com. Default: AZURE_OPENAI_ENDPOINT
# api = "responses"         # or "chat". Default: Responses for codex and -pro deployments only
# api_version = "preview"   # appended as ?api-version=; the v1 API needs none

[providers.foundry]
resource = "my-foundry"     # https://my-foundry.services.ai.azure.com. Default: AZURE_FOUNDRY_RESOURCE

Costs: Claude on Bedrock, Vertex AI and Foundry is priced like the Anthropic API, and Azure deployments named after an OpenAI model are priced like OpenAI. Regional-endpoint and data-zone premiums are not modeled. Common Bedrock models (gpt-oss, Nova, Llama 3.3, DeepSeek R1) and Vertex Gemini 2.5 models have prices. Other models show as unknown; add them under [pricing].

Amazon Bedrock

sh
export AWS_PROFILE=ml-dev AWS_REGION=us-east-1      # or AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY[/AWS_SESSION_TOKEN]
chanto -m bedrock/us.anthropic.claude-sonnet-4-5-20250929-v1:0
chanto -m bedrock/openai.gpt-oss-120b-1:0           # Converse
  • Model IDs. Newer models are invoked through an inference profile: us., eu. or apac. for a geography, or global.. Use one of these instead of the base model ID (anthropic.claude-…). Application inference profile ARNs also work. Set api = "anthropic" when the ARN doesn't name a Claude model. Enable each model under Model access in the Bedrock console for the region. /models lists the profiles and on-demand models the account can call, if [providers.bedrock] is configured.

  • Credentials, in order:

    1. A Bedrock API key: AWS_BEARER_TOKEN_BEDROCK, chanto login bedrock, or api_key in the section. It is sent as a bearer token.
    2. The profile named in Chanto config.
    3. AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY/AWS_SESSION_TOKEN.
    4. The AWS_PROFILE (or default) profile in ~/.aws/credentials and ~/.aws/config: static keys, credential_process, or an IAM Identity Center session from aws sso login.
    5. Container credentials (ECS/EKS) and EC2 instance profiles (IMDSv2).

    Profiles that need anything else (assume-role chains, web identity) are resolved by running aws configure export-credentials, which needs AWS CLI 2.9 or later. AWS_CONFIG_FILE, AWS_SHARED_CREDENTIALS_FILE, AWS_ENDPOINT_URL_BEDROCK_RUNTIME and AWS_ENDPOINT_URL_BEDROCK are honored.

  • IAM permissions: bedrock:InvokeModelWithResponseStream on the foundation models and inference profiles you use. Cross-region profiles need it on the profile ARN and on the foundation model in each destination region. Optional: bedrock:ListInferenceProfiles and bedrock:ListFoundationModels, for /models and the chanto doctor check. Without them doctor reports a permission note, and requests still work.

json
{"Version": "2012-10-17", "Statement": [
  {"Effect": "Allow", "Action": ["bedrock:InvokeModelWithResponseStream", "bedrock:InvokeModel"],
   "Resource": ["arn:aws:bedrock:*::foundation-model/*", "arn:aws:bedrock:*:<account-id>:inference-profile/*"]},
  {"Effect": "Allow", "Action": ["bedrock:ListFoundationModels", "bedrock:ListInferenceProfiles"], "Resource": "*"}]}

Google Vertex AI

sh
gcloud auth application-default login                # or GOOGLE_APPLICATION_CREDENTIALS=/path/to/key.json
export GOOGLE_CLOUD_PROJECT=my-project CLOUD_ML_REGION=global
chanto -m vertex/claude-sonnet-4-5@20250929
chanto -m vertex/gemini-2.5-pro                     # sent as google/gemini-2.5-pro
chanto -m vertex/meta/llama-4-maverick-17b-128e-instruct-maas
  • Model names. Claude models use Vertex's names (claude-sonnet-4-5@20250929) and must be enabled in Model Garden for the project. A bare Gemini name gets the google/ publisher prefix. Other Model Garden models are given as publisher/model. For global, requests go to aiplatform.googleapis.com. For any other region, they go to <region>-aiplatform.googleapis.com. Claude is available in global and in specific regions such as us-east5 and europe-west1.
  • Credentials: Application Default Credentials. User credentials (authorized_user) are refreshed with their refresh token. Service account keys sign an RS256 JWT locally. Other types (workload identity federation, impersonation) and machines without an ADC file run gcloud auth application-default print-access-token or gcloud auth print-access-token. chanto login vertex doesn't apply, since there are no keys.
  • IAM: roles/aiplatform.user (Vertex AI User) on the project, which includes aiplatform.endpoints.predict. The project needs the Vertex AI API (aiplatform.googleapis.com) enabled. The doctor check reads the project's location (GET …/projects/<project>/locations/<region>). If it reports a permission note while requests work, the role is narrower than Vertex AI User.

Azure OpenAI and Azure AI Foundry

sh
export AZURE_OPENAI_ENDPOINT=https://my-openai.openai.azure.com AZURE_OPENAI_API_KEY=…
chanto -m azure/my-gpt-5-deployment                 # the deployment name, not the model name

export AZURE_FOUNDRY_RESOURCE=my-foundry AZURE_FOUNDRY_API_KEY=…
chanto -m foundry/claude-sonnet-4-5                 # the deployment name in Foundry
  • Azure OpenAI uses the v1 API (https://<resource>.openai.azure.com/openai/v1/) with the api-key header. AZURE_OPENAI_DEPLOYMENT adds your deployment to /model. Requests send max_completion_tokens, and optional parameters a deployment rejects (such as temperature on reasoning models) are dropped and the request is retried.
  • Foundry sends Claude to https://<resource>.services.ai.azure.com/anthropic/v1/messages with x-api-key. Other models go to …/openai/v1/chat/completions. ANTHROPIC_FOUNDRY_RESOURCE, ANTHROPIC_FOUNDRY_API_KEY and ANTHROPIC_FOUNDRY_BASE_URL are accepted too.
  • Without an API key, Chanto uses a Microsoft Entra ID token for https://cognitiveservices.azure.com. It comes from a service principal (AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET), or else from the Azure CLI (az login, then az account get-access-token). The Azure CLI is optional and only needed for this path.
  • Roles: with Entra ID, the identity needs Cognitive Services OpenAI User (Azure OpenAI) or Azure AI User (Foundry) on the resource. With an API key, no role is needed.

Live smoke test

The test suite never contacts a cloud and never reads your real credentials. An opt-in test makes one small real request per configured cloud, using your environment:

sh
CHANTO_LIVE_CLOUD=1 CHANTO_LIVE_BEDROCK_MODEL=us.anthropic.claude-haiku-4-5-20251001-v1:0 \
  CHANTO_LIVE_VERTEX_MODEL=gemini-2.5-flash CHANTO_LIVE_AZURE_MODEL=<deployment> CHANTO_LIVE_FOUNDRY_MODEL=<deployment> \
  cargo test live_cloud_smoke -- --nocapture

Edit this page on GitHub Report a problem

Commands, settings, errors: type a few words.