Hire Team

INDUSAGI DOCUMENTATION

Getting Started

Preserved documentation snapshot. APIs and capabilities differ by edition and version. Check the selected package before implementation. View pinned source.

View Rust Coding Agent source on GitHub
On this page

induscode is the 100%-Rust rebuild of the terminal-first AI coding agent, built on the Rust indusagi framework. Build it with cargo build, install it with cargo install induscode, then run the indusr (or indusagir) binary — a bare invocation opens the interactive ratatui console, and flags switch it into one-shot print or headless line-protocol modes. State lives under ~/.indusagi/agent/.

Table of Contents

Toolchain

The agent is pinned to one stable Rust release, in rust-toolchain.toml at the repo root:

[toolchain]
channel    = "1.96.0"
components = ["rustfmt", "clippy"]

The pin matches the framework's MSRV floor (rust-version = "1.96", edition 2024) so the agent never out-runs the framework it depends on. induscode is its own virtual Cargo workspace; its product crate depends on the already-complete indusagi framework crate (indusagi = { version = "0.1.0", features = ["swarm"] }), exactly as the TypeScript agent declared "indusagi": "^0.13.1".

Build from source

cargo build exercises the default feature surface ["rustls", "mcp", "tui", "oauth"] — TLS over rustls, the MCP bridge, the ratatui console, and the browser sign-in flows:

cargo build --workspace
# headless (no console / no render layer):
cargo build -p induscode --no-default-features --features "rustls,anthropic"
# release binary (the dist profile builds with lto = "fat"):
cargo build --release -p induscode

# run straight from source without installing (dev launch):
cargo run -p induscode --bin indusr -- --help
cargo run -p induscode --bin indusr -- "fix the failing test in src/auth"

The repo-automation tasks live in xtask — the lineage / version single-source gates that keep the clean-room posture honest:

cargo run -p xtask -- lineage     # clean-room source-hygiene gate
cargo run -p xtask -- version     # literal-VERSION single-source gate

The version is single-sourced: induscode::core::VERSION is env!("CARGO_PKG_VERSION"), resolved at compile time from Cargo.toml, never a string literal in source (an xtask/CI grep gate forbids any version literal outside Cargo.toml).

Install

Audience Command Result
Rust user (crates.io) cargo install induscode installs indusr + indusagir
Rust user, prebuilt cargo binstall induscode downloads a release binary
Embedding it as a library cargo add induscode adds the dependency (surface induscode::<module>)
npm user (preserves the habit) npm i -g indusagi-coding-agent the published package wrapper
macOS / Linux, scripted curl --proto '=https' -sSf https://…/install.sh | sh scripted install
Windows irm https://…/install.ps1 | iex scripted install

Installing the crate gives you two equivalent commands — indusr and indusagir — both built from the single src/bin/main.rs entry (the Rust analogue of the TS bin block {"indus": …, "indusagi": …}), both launching the same agent. Use whichever you prefer.

The binary and invocation

The binary's main is async fn main() -> ExitCode — the single place an exit code reaches the OS. It is the only OS exit point: returning [ExitCode] lets the tokio runtime drain and destructors run, so the terminal's raw / alt-screen mode is always restored on the way out (a process::exit mid-TUI would corrupt it).

main runs four steps, mirroring the TS entry.ts + boot.ts:

  1. Brand off argv[0]'s basename (resolve_brand): the live brand is whichever bin name launched the process, falling back to the primary BIN_NAMES[0] ("indus") when the launch name is unrecognized.
  2. Install the single [MCP] console-noise filter (install_mcp_noise_filter), a no-op when the INDUSAGI_DEBUG env var is set so diagnostics flow freely.
  3. Route the argv (route): the no-session verbs (auth, signin, signout, the package verbs, --list-models) are owned by the CLI and run to an exit code without touching boot; --help / --version short-circuit; everything else is a real run.
  4. Hand a real run to [boot_run] (induscode::boot::boot_run), whose resolved ExitCode this main adopts unchanged.

--help / -h renders the usage banner from the single declarative flag table via render_usage() (so the help text can never drift from what the parser accepts), then appends a Commands: footer documenting the credential verbs. --version / -v prints <app name> <version> from the single-source VERSION:

indusr --help        # the option reference, generated from the flag table
indusr --version     # prints: indusagi <version>
indusagir --version  # the second bin name prints the same

An unknown leading flag (a -… token the launch table does not name) is a usage error — exit code 2, the agent's malformed-invocation convention. Note the parser itself (induscode::launch::parser::read_invocation) is deliberately tolerant of unknown flags everywhere else: an unrecognized --flag lands in the loose flag bag as a boolean (the extension-flag escape hatch), never an error.

Set a provider key

The fastest path to a first run is to export the provider key for the model you intend to use, then run. Each provider in the credential directory (induscode::launch::credentials::PROVIDER_DIRECTORY, 14 entries) reads its own conventional environment variable:

export ANTHROPIC_API_KEY="sk-ant-..."   # Anthropic (Claude) — the default provider
export OPENAI_API_KEY="sk-..."          # OpenAI
export GEMINI_API_KEY="..."             # Google Gemini

The directory maps each provider id to its env_key and a docs_url where you obtain a key:

Provider id Label Env var
anthropic Anthropic (Claude) ANTHROPIC_API_KEY
openai OpenAI OPENAI_API_KEY
google Google Gemini GEMINI_API_KEY
xai xAI (Grok) XAI_API_KEY
groq Groq GROQ_API_KEY
cerebras Cerebras CEREBRAS_API_KEY
mistral Mistral MISTRAL_API_KEY
openrouter OpenRouter OPENROUTER_API_KEY
minimax MiniMax MINIMAX_API_KEY
kimi Kimi (Moonshot) MOONSHOT_API_KEY
sarvam Sarvam SARVAM_API_KEY
krutrim Krutrim KRUTRIM_API_KEY
nvidia NVIDIA NVIDIA_API_KEY
zai Z.ai ZAI_API_KEY

With a key in the environment you can run immediately — no sign-in step required. For persistent, multi-account, or browser (OAuth) credentials, use signin instead (next section). See Auth for the full credential model.

Sign in interactively

The signin / signout verbs are no-session credential commands the CLI owns: the router intercepts a leading signin / signout token before the boot handoff (boot would otherwise drop them into the REPL), runs the agent's own multi-account credential command, and returns its own exit code — 0 on success, 1 on a typed fault printed to stderr. The auth subcommand (auth login / auth status / auth refresh / auth logout) is dispatched the same way.

indusr signin anthropic                    # store an Anthropic API key (the default path)
indusr signin openai --account work        # name a stored credential account
indusr signin anthropic --method oauth     # force the browser (OAuth) path
indusr signin anthropic --oauth            # shorthand for --method oauth
indusr signin --list                       # read-only: list saved accounts
indusr signout anthropic                   # remove a stored credential

indusr auth login anthropic                # framework PKCE browser flow (where configured)
indusr auth status [provider]              # show which providers are signed in
indusr auth logout                         # routed to the agent's signout path

The credential command verbs are recognised by induscode::launch::credentials (run_credential_command), which accepts --provider / --account / --method <oauth|api-key> (alias --api-key / --oauth) / --list, and treats the first bare positional after the verb as the provider. The api key is read with echo muted on a TTY (crossterm raw mode for the duration of the read) and wrapped in a SecretString immediately, so it never logs or echoes.

Default-to-api-key: the shipped OAuth client ids are unconfigured sentinels, so a bare indusr signin <provider> automatically appends --method api-key (default_signin_to_api_key) and lands straight on the working api-key prompt. Pass --method oauth explicitly to opt into the browser flow. Credentials persist to the multi-account auth.json vault under ~/.indusagi/agent/, byte-compatible with the framework's single-record auth store.

First interactive run

A bare indusr invocation — or any invocation whose resolved mode is text — opens the interactive console, a ratatui REPL driven by the conductor: streaming replies, a live tool deck, and slash commands. The output mode OutputMode::Text maps to the boot runner RunnerId::Repl (induscode::boot::pipeline).

indusr                                  # interactive ratatui console
indusr "refactor the auth module"       # seed the first prompt, stay interactive
indusr -i "start here"                  # force interactive even with a prompt

--interactive / -i forces the interactive session even when a prompt is supplied (it overrides --print, keeping the mode text). Resume or continue a prior session from the directory:

indusr --resume        # -r : pick a previous session to resume
indusr --continue      # -c : continue the most recent session in this directory

Attach files to any prompt with @path references — the reader collects them into a separate file-reference channel (read_file_references), and the attachment gatherer inlines text files into the prompt and decodes images to the framework image block:

indusr "review this" @src/main.rs @diagram.png

The @file token is neither a flag nor a positional, and the -- terminator stops option parsing so every later token is treated as a positional.

One-shot print mode

-p (--print) runs a single request to settlement, prints only the final result, and exits — ideal for scripting and pipelines. The resolved mode is OutputMode::Json, which maps to the boot runner RunnerId::Oneshot in its clean-text shape.

indusr -p "summarize this repo"
indusr -p "explain the boot pipeline" @src/bin/main.rs
indusr -p --json "summarize this repo"   # NDJSON shape (see below)

The production output seam (StdioSink) writes user-facing text to stdout and diagnostics to stderr, flushing each write so output is not buffered behind a long-running turn. A closed pipe is swallowed, so piping through | head exits cleanly.

Headless line-protocol mode

Adding --json (alias --rpc) selects the headless line protocol over stdio for a driving parent process — a newline-delimited JSON wire. The resolved mode is OutputMode::Rpc, which maps to the boot runner RunnerId::Link. See Channels for the full wire surface.

indusr --json                            # long-lived line-protocol link
indusr -p --json "summarize this repo"   # one-shot print run, NDJSON output

The production input seam (StdinSource) performs one blocking stdin read per line on a blocking thread (so the async runtime is not stalled), dropping the trailing newline. Only the interactive runner reads it; the one-shot and link runners drive their own framers and never read stdin line-by-line.

How the mode is derived

The output mode resolves from your flags via the fixed derive_mode precedence that induscode::launch::parser::read_invocation applies, then maps to a boot runner:

Flags OutputMode Runner (RunnerId)
--json / --rpc Rpc Link (line protocol over stdio)
--print (-p) without --interactive Json Oneshot
anything else (the default) Text Repl (interactive ratatui console)

--json wins over --print; --interactive (-i) overrides --print to keep the mode Text. The runner registry (induscode::boot::contract) asks each runner whether it accepts the parsed invocation and runs the first match, falling back to Repl when nothing accepts.

Pick a model

-m (--model) selects the model for the run, matched against the framework catalog, provider-qualified (provider/name) or bare:

indusr -m claude-haiku-4-5 -p "hi"
indusr -m anthropic/claude-sonnet-4 "refactor the auth module"
indusr -m openai/gpt -p --json "summarize this repo"

Pair -m with --thinking to set the reasoning effort. The accepted rungs are the THINKING_EFFORTS vocabulary (induscode::launch::contract::ThinkingEffort): off, minimal, low, medium, high, xhigh. An unrecognized value is ignored.

indusr -m claude-sonnet-4 --thinking high "design the migration plan"

--fallback-model names a model to switch to mid-turn when the selected model is overloaded (HTTP 529). With no -m, the model falls back to default_model / default_provider from your settings (whose default provider is anthropic), then to the framework default. See Models and the framework LLM gateway for provider routing.

Browse the model catalog

--list-models prints the available models as an aligned table and exits, with no session and no directory side effects. The CLI handles it before the boot handoff via induscode::launch::catalog::print_model_catalog, sourcing rows from the live facade catalog (RegistrySource, walking get_providers() × get_models() over the framework indusagi catalog). Pass an optional substring to filter:

indusr --list-models             # the full provider/model catalog
indusr --list-models claude      # filter by a case-insensitive substring
indusr --list-models=opus        # inline substring filter

The substring is a plain case-insensitive test over the provider/modelId identifier (no fuzzy matcher). The six table columns are Provider, Model, Context, Max Output, Thinking, and Images — each row's context_window, max_tokens, reasoning, and image-modality support projected from the facade model (facade_model_to_row). The mock echo provider is excluded (EXCLUDED_PROVIDERS) so the listing shows only the real matrix.

Where state lives

Per-user state lives under ~/.indusagi/agent/ by default. The coding agent nests its state one level deeper than the framework Locator (<home>/.indusagi): the brand's profile_dir_name is .indusagi and its state_dir_name is agent, so the resolved profile root is <home>/.indusagi/agent. Override the location with the INDUSAGI_CODING_AGENT_DIR environment variable (~-expanded, cwd-relative-aware).

The fully-resolved layout is one Workspace record of absolute paths (induscode::core::create_workspace):

Member Path under the profile root What it holds
settings_path settings.json merged user preferences
auth_path auth.json the multi-account credential vault
sessions_dir sessions/ per-cwd transcript directories
models_path models.json custom model-catalog overrides
tools_dir / bin_dir bin/ provisioned native helpers (fd / rg)
prompts_dir prompts/ user-authored prompt / command templates
themes_dir themes/ user-installed colour themes
export_template_dir export-template/ HTML transcript-export template
debug_log_path debug.log verbose diagnostic log
mcp_config_path mcp-servers.json external MCP server config
memory_config_path memory.json memory-feature config
composio_config_path composio.json Composio-integration config
memory_db_path memory.db on-disk memory database

Settings are read through a two-tier PreferenceStore (induscode::core::PreferenceStore): project-over-global-over-default on read, with writes landing in the project tier only (<cwd>/.indusagi/settings.json), so a per-checkout override never leaks machine-wide. A missing / unreadable / bad-JSON / non-object settings file degrades to the empty record rather than throwing. See Settings for the 24-key Preferences record and the merge.

Notes

  • The crate is induscode on crates.io; the two installed binaries are indusr (primary) and indusagir, both built from the single src/bin/main.rs entry.
  • Requires the Rust toolchain pinned in rust-toolchain.toml (channel 1.96.0, MSRV 1.96, edition 2024).
  • --help and --version are rendered from the same FLAG_SPECS table the parser reads (induscode::launch::flags, 18 rows in 5 groups), so usage text can never drift from behavior.
  • The version is single-sourced from Cargo.toml via env!("CARGO_PKG_VERSION"), re-exported as induscode::core::VERSION — never duplicated; an xtask gate forbids version literals outside Cargo.toml.
  • State lives under ~/.indusagi/agent/ by default; relocate it with INDUSAGI_CODING_AGENT_DIR. Set INDUSAGI_DEBUG to unmute the [MCP] transport-log filter.
  • The agent is built on the Rust indusagi framework and reaches it as indusagi::<module> (core, llmgateway, runtime, tui, shell_app, …). For the full flag grammar and mode documentation see Launch and the CLI reference. Parity with the other editions is documented at Python CLI and the TS edition.