Parity & Lineage
induscode(Rust) is a clean-room, 100%-Rust rebuild of the terminal-first AI coding agent that ships as the npm packageindusagi-coding-agent, continuing its0.2.xproduct line and built entirely on the publishedindusagiframework crate. It is authored from a behavioral spec plus the framework's public crate API — never transliterated from the TypeScript or Python editions. This page records the parity story area by area, the milestone build history, the performance posture, and three CI-blocking hygiene gates (lineage,framework-via-deps,version) that keep the clean-room claim honest.
Table of Contents
- Provenance
- The Three Editions
- Relationship to the Rust Framework
- Parity by Area
- Milestone Build History
- Clean-Room Posture & Gates
- Single-Source Version
- Library Swaps
- Performance
- Doc Drift & Known Notes
- Source Files
Provenance
induscode (Rust) is an independent implementation written against a behavioral spec plus the public indusagi crate API. No third-party application source was copied; it reuses no third-party application source code. From the project NOTICE:
This product is an independent, clean-room implementation, designed and written from scratch against a behavioral specification and the public API of the indusagi framework. It reuses no third-party application source code.
The agent reaches the framework only through declared crate dependencies — never a vendored copy or a source include — and that is a CI-checked invariant (cargo run -p xtask -- framework-via-deps). The lineage scan re-derives its marker set for a Rust tree rather than copying the TS gate's JavaScript-identifier markers, which do not exist here:
// crates/induscode/src/core/guardrails.rs
pub const FORBIDDEN_MARKERS: &[&str] = &["pi-mono", "Mario Zechner", "pimono"];
The provenance travels with redistributions: the MIT LICENSE, the NOTICE, and CREDITS.md must accompany any copy, and the shipped binary's transitive-dependency license bundle is generated by cargo-about into THIRDPARTY at release time.
The Three Editions
induscode exists in three independent clean-room editions over the same indusagi framework, each in its own language and packaging. The Rust edition is the youngest and the fastest.
| Edition | Package | Bins | Framework | Docs |
|---|---|---|---|---|
| TypeScript (original product line) | indusagi-coding-agent (npm) |
indus / indusagi |
"indusagi": "^0.13.1" (npm) |
/cli |
| Python (clean-room port) | induscode (PyPI) |
pindus / induscode |
indusagi[mcp,tui] (PyPI) |
/python-cli |
| Rust (this edition) | induscode (crates.io) / indusagi-coding-agent (npm shim) |
indusr / indusagir |
indusagi = "0.1.0" (crates.io) |
/rust-cli |
The Rust edition carries the product version across from the npm line (0.2.x) rather than restarting at 0.1.0, because crates.io and npm are independent namespaces. The TypeScript releases this Rust line continues, newest-first, are:
- 0.2.2 — MiniMax-M3 model; local framework dependency for iteration.
- 0.1.62 — rich markdown rendering, syntax-highlighted code, GFM tables, flicker-free streaming, colored/word-level diffs, color-blind themes, live theme preview, single-source version.
- 0.1.61 — fixed the silent no-op when installed via a global npm symlink (realpath the entry-point check).
- 0.1.60 — published-framework dependency; normalized the
indus/indusagibin shims.
Relationship to the Rust Framework
The agent runs entirely on the indusagi framework, pulled in as the single published umbrella crate (crates.io 0.1.0) — the Rust analogue of the TypeScript agent's "indusagi": "^0.13.1" dependency. Every former framework module is reached as indusagi::<module>:
# crates/induscode/Cargo.toml
indusagi = { version = "0.1.0", features = ["swarm"] }
The framework supplies the LLM API abstraction (the LLM gateway), the agent core the conductor wraps, the TUI render primitives the console draws over, the MCP client, and observability. induscode keeps only its own seams — the conductor's turn loop and signal projection, the branchable transcript, the auth vault, signal translation, and the capability deck — and reuses everything else wholesale. The framework workspace is frozen and untouched by this rebuild.
The agent owns its own brand record rather than reusing indusagi::core::brand::BRAND, because the coding-agent identity differs from the framework shell's — two bin names, a nested agent state dir, and the INDUSAGI_CODING_AGENT_DIR override:
// crates/induscode/src/core/brand.rs
pub const BRAND: Brand = Brand {
app_name: "indusagi",
label: "indusagi",
profile_dir_name: ".indusagi",
state_dir_name: "agent",
bin_names: ["indus", "indusagi"],
env_prefix: "INDUSAGI",
env_profile_dir: "INDUSAGI_CODING_AGENT_DIR",
env_debug: "INDUSAGI_DEBUG",
env_share_viewer: "INDUSAGI_SHARE_VIEWER_URL",
share_viewer_url: "https://buildwithindusagi.ai/session/",
};
The shared ~/.indusagi/agent profile root can never drift from the framework's because the env-var prefix and debug flag are derived from one record. For the boot pipeline that wires these together, see boot and launch.
Parity by Area
The Rust port was delivered milestone by milestone, each a complete, tested layer authored against a frozen type seam (the contract module every collaborator codes against). The verdict is full feature parity on the user-facing surface — every CLI flag, the slash-command catalog, every console overlay, settings, sessions, channels, addons, the capability deck, and the headless channels — re-architected onto Rust / ratatui / framework idioms rather than line-by-line translated.
| Area | Parity | Notes |
|---|---|---|
| CLI flags + conventions | full (18 specs) | The single declarative FLAG_SPECS table (launch::flags) drives both the parser and the usage renderer, so --help can never drift from what the parser accepts. Includes --print/-p, --json, --interactive/-i, --model/-m, --fallback-model, --account, --thinking, --list-models, --cwd, --system, --append-system, --resume, --continue, --tools, --no-tools, --mcp, --help/-h, --version/-v, plus @file attachments, -- terminator, -pi clustering, and unknown-flag tolerance. See launch. |
| Subcommands | full | auth login / status / refresh (framework command) and auth logout (agent vault); signin / signout interactive credential verbs; install / remove / update / list / config package management — all no-session verbs the CLI owns and runs to an exit code without touching boot. See auth. |
| Slash commands | full (static + dynamic) | A pure derived &[SlashCommand] catalog — three topic families (transcript, workbench, integrations) plus dynamic /skill:<name> per capability card and /<template> per macro, with a reserved-token collision guard. See slash commands. |
| Console: overlays, theme, input, reducer | full (12 overlays) | One immutable ConsoleState, one pure console_reducer (exhaustive match, no I/O), immediate-mode ratatui draw, and 12 overlays keyed by ModalKind (Settings, Models, ScopedModels, Theme, Sessions, Tree, UserTurns, SignIn, SignOut, Oauth, Plugin, Approval — plus the None no-overlay state) mirrored onto the framework DialogStack. See dialogs and theming. |
| Channels (headless) | full | One-shot --print (clean final text) and the JSON-RPC 2.0 / NDJSON --json line protocol over the separator-safe framer (U+2028/U+2029 pin), both standalone with the console crate absent (--no-default-features). See channels. |
| Capability deck + addons | full | A management layer over the framework's 12 built-in tools (wire names read/ls/grep/find/websearch/webfetch/todo_read · write/edit/bash/process/todo_set), one source-of-truth CAPABILITY_CARDS catalog, app-novel cards (todo ledger, process table, plan markers, sub-agent delegation, SaaS, memory), and an event-sourced MCP enrollment ledger. See capability deck and addons. |
| Conductor (the brain) | full | Wraps indusagi::runtime::Agent, projects the framework's 7-event RunEvent stream to a stable SessionSignal stream, persists a branchable transcript, retries transient faults with backoff, swaps to a fallback model on HTTP 529, and drives window-budget compaction via the CondenseFn seam. See conductor. |
| Sessions, transcripts, export | full | Branch-aware product transcript over the framework SessionStore DAG, resume/continue by id, and self-contained HTML/markdown /export. See sessions and transcript export. |
| Window-budget + insight | full | The deterministic create_condenser (token gate is_over_budget, shared AUTO_CONDENSE_POLICY) drops into the conductor's compaction seam; insight is a thin wrapper over framework tracing adding only a fan-out channel, a collector sink, an agent scrubber, and an NDJSON replay reader. See window budget and insight. |
| Runtime-bridge | full (library) | The per-model decision point: a turn through either the framework gateway or an external child runtime (claude / codex / a peer indusagi), implemented as a ModelInvoker so the conductor cannot tell which path produced the turn. The OS-touching child transport is gated behind the off-by-default cli-bridges feature. See runtime bridge. |
| Models & providers | full | The model/provider catalog is the framework's, projected by launch::catalog; the MiniMax-M3 model is carried over (minimax / minimax-cn providers; per-provider preferred-default ordering MiniMax-M2, MiniMax-M2.7). See models. |
| Auth vault | full | A disk-backed multi-account vault over ~/.indusagi/agent/auth.json that reconciles BOTH the framework single-record { providers: { … } } shape AND the agent's nested provider→account→record shape, so an api-key signin is visible to auth status. See auth. |
Milestone Build History
The build proceeded milestone by milestone (named after the subsystem each one completed), every stage scaffolded against a frozen contract and then filled by per-module writers and test-gated. The milestone names appear in each module's header doc-comment:
| Milestone | Module(s) | What it delivered |
|---|---|---|
| M0 | core (workspace), brand, guardrails, xtask |
The leaf primitives, the single-source VERSION, the lineage marker set, and the repo-automation gates. |
| M-launch | launch |
The 18-row FLAG_SPECS table, the tolerant parser + usage renderer, @file attachments, credentials, the OAuth seam, and the model catalog projection. |
| M-settings | core::{kit, settings, sessions} |
The four sub-1k-LOC TS subsystems folded into core: the PreferenceStore, the saved-session tree, and the shell/image/tool-fetch kit. |
| M-deck | deck |
The capability deck: built-in bridging, the CAPABILITY_CARDS catalog, profile provisioning, app-novel cards, and the MCP enrollment ledger. |
| M-window-budget | window_budget |
The deterministic condenser, the token gate, and AUTO_CONDENSE_POLICY. |
| M-briefing | briefing |
System-prompt / context assembly. |
| M-conductor | conductor |
The brain: the streaming turn loop, retry/backoff/fallback, signal projection, the branch-aware transcript, and the session ops. |
| M-runtime-bridge | runtime_bridge |
The provider-routing ModelInvoker broker + external-child transport. |
| M-channels | boot + channels |
The first runnable milestone: the boot orchestrator (runner registry), the one-shot --print channel, and the JSON-RPC --json link channel. |
| M-console | console |
The ratatui REPL re-architecture: immutable state, pure reducer, immediate-mode draw, the 12 overlays. |
| M-insight | insight |
The observability wrapper over framework tracing. |
| M-export | transcript_export |
Self-contained HTML/markdown transcript publishing. |
| M-final (the merge) | all | The 13 former induscode-* library crates were merged into the single crates/induscode product crate (each a pub mod), the 13 indusagi-* framework path deps collapsed to the one published indusagi umbrella crate, and the two [[bin]] targets pointed at one src/bin/main.rs. |
The test suite is sizeable and runs network-free: the crate tree carries on the order of 1,300+ test functions (#[test] / #[tokio::test]), spanning the pure reducer, the slash catalog snapshots, the parser/usage golden tests, the credential-vault round-trips, and the assert_cmd golden CLI tests (indusr --help / --version byte-exact). The clean-room and single-source gates are themselves test-asserted (e.g. the_workspace_tree_reaches_framework_only_via_deps).
Clean-Room Posture & Gates
Three blocking CI gates run via cargo run -p xtask -- <command>, the Rust analogue of the TS scripts/lineage-scan.mjs and build.mjs:
cargo run -p xtask -- lineage # clean-room source-hygiene gate (exit 0 = clean)
cargo run -p xtask -- framework-via-deps # framework reached ONLY via declared crate deps
cargo run -p xtask -- version # forbid any string-literal version outside Cargo.toml
| Gate | Module | What it checks |
|---|---|---|
lineage |
xtask::lineage |
Walks crates/** plus xtask/ and the root Cargo.toml over the owned suffixes (.rs, .toml, .md, .json), case-insensitively, and fails on any FORBIDDEN_MARKERS hit. Skips the two definition sites (crates/induscode/src/core/guardrails.rs, xtask/src/lineage.rs). |
framework-via-deps |
xtask::framework_via_deps |
Fails on any include! / include_str! / include_bytes! / #[path = …] whose argument reaches into ../indusagi-rust/crates/*/src — a source-include into the sibling framework workspace. Declared indusagi crate deps in .toml are the allowed mechanism and are not flagged. |
version |
xtask::version |
Walks crates/**/*.rs and xtask/**/*.rs and fails on any string-literal SemVer triple in runtime source (comments and #[cfg(test)] blocks skipped; // xtask:allow-version is the escape hatch). The only place a version literal may live is the root Cargo.toml. |
This is the Rust collapse of the TS three-source version-drift bug class: the runtime VERSION is env!("CARGO_PKG_VERSION"), so it cannot disagree with the manifest, and the gate forbids any competing literal from being introduced.
Single-Source Version
The version lives in exactly one place — [workspace.package] version in the root Cargo.toml — and is read back at compile time:
// crates/induscode/src/core/workspace.rs
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
// crates/induscode/src/bin/main.rs — the `--version` route
if invocation.version {
println!("{} {VERSION}", BRAND.app_name); // e.g. "indusagi 0.1.2"
return ExitCode::SUCCESS;
}
Note that the workspace declares version = "0.2.2" (continuing the npm indusagi-coding-agent 0.2.x line) while the merged crates/induscode package currently pins version = "0.1.2"; --version reports the crate's own value. Treat the per-document version figure as the snapshot at that document's date. The xtask version gate guarantees no third figure can be hard-coded into runtime code.
Library Swaps
The TS dependencies (react / ink / chalk / marked / highlight.js / ulid / jiti / …) have no Rust counterpart, so the stack was re-derived for Rust during the port. Third-party pins are copied verbatim from the framework workspace so the agent and framework resolve exactly one tokio / serde / reqwest / ratatui (enforced by cargo deny check bans).
| TS dependency | Rust replacement |
|---|---|
| Ink / React (console) | ratatui + crossterm (immediate-mode REPL) |
marked (markdown) |
pulldown-cmark (markdown → SGR/HTML) |
highlight.js (code) |
syntect + two-face (syntax highlighting) |
| diff rendering | similar (colored/word-level diffs) |
ulid |
ulid crate (bridge-key minting) |
| jiti virtual modules | declarative manifest.toml addons via toml |
| native-tls / OpenSSL | reqwest pinned rustls-tls — no OpenSSL anywhere in the tree |
clap (full CLI) |
clap for help/version text only (the parser is the launch flag table) |
The async runtime is tokio; serialization is serde / serde_json (with preserve_order for byte-exact JSON wire parity). The mcp and tui framework features are on by default; composio, swarm, and the OS-touching cli-bridges are feature-gated. The agent ships no compile-time data assets — themes are code, prompts are string literals, and skills are discovered from the user's filesystem at runtime — so there is no bundler, no tsc .d.ts emit, and no copy-assets step.
Performance
The Rust edition replaces the Node toolchain surface entirely (build.mjs esbuild bundling, tsc --emitDeclarationOnly, the shebang / chmod / import.meta.url entry guard, process.exit + the 1s unref'd safety timer, the engines.node >= 20 / ESM-interop constraints) with fn main() -> ExitCode, a committed Cargo.lock, and rust-version = "1.96". The single OS exit point is the binary's async fn main; boot_run never calls process::exit, mapping every outcome to an ExitCode and always draining its closables so the terminal's raw/alt-screen mode is restored deterministically on the way out.
The release profile is tuned for an interactive, latency-sensitive agent rather than for size:
# Cargo.toml
[profile.release]
opt-level = 3 # latency-sensitive agent — NOT "z"
lto = "thin"
codegen-units = 1
strip = true
panic = "abort"
[profile.dist] # release-artifact profile (max optimization)
inherits = "release"
lto = "fat"
The dist (cargo-dist) release matrix produces static-musl / rustls binaries (no OpenSSL) for aarch64/x86_64 apple-darwin, linux-musl, and windows-msvc, plus shell / PowerShell / Homebrew / npm installers (the npm shim preserves the npm i -g indusagi-coding-agent habit). An opt-in mimalloc global allocator is gated off by default, flipped on only once an A/B measurement shows it wins on the agent's allocation-heavy console/streaming hot paths. The same architectural lineage that makes the broader Rust rebuild markedly faster and leaner than the Node edition applies here; see the Rust framework parity page for the framework-side performance record.
Doc Drift & Known Notes
This rebuild's own metadata documents drift slightly, by design (each is a snapshot at its date):
- Binary names. The authoritative
[[bin]]targets incrates/induscode/Cargo.tomlareindusrandindusagir. The crate'sREADME.mdand the getting-started docs use these names; theCHANGELOG.mdandCREDITS.mdheaders still sayindus/indusagi(the npm-line names the brand record'sbin_namesarray also carries). Both ship from onesrc/bin/main.rs; the live brand is whichever nameargv[0]was launched under, falling back toBIN_NAMES[0](indus). - Version figures. The workspace is
0.2.2, the merged crate is0.1.2;--versionreports the crate value (indusagi 0.1.2). - OAuth default is api-key. The shipped OAuth client ids are unconfigured sentinels in a default build, so
indusr signin <provider>appends--method api-keyto land on the working api-key prompt; an explicit--method oauth(or--oauth) is preserved.auth loginprints a one-line tip pointing at the api-key path before handing off to the framework flow. This is a deliberate ergonomics divergence, not a gap. auth logoutandauth statusare agent-owned. The frameworkauthcommand has nologoutverb and itsstatusreader sees only the top-level{ providers: { … } }shape; the agent routes both to its own multi-account vault reader so api-key sign-ins are visible.
Source Files
| File | Role |
|---|---|
README.md |
The crate overview — install matrix, run modes, architecture, clean-room posture. |
CHANGELOG.md |
Release log for the Rust rebuild ([0.2.2] — Rust rebuild) and the TypeScript lineage it continues. |
CREDITS.md |
Provenance: independent clean-room implementation, the framework relationship, and the third-party runtime dependency list. |
NOTICE |
The notice that must travel with redistributions. |
LICENSE |
MIT. |
THIRDPARTY |
The transitive-dependency license bundle (generated by cargo-about at release time). |
Cargo.toml (root) |
The virtual workspace manifest — single-source version, the indusagi = "0.1.0" framework dep, the unified third-party pins, and the release/dist profiles. |
crates/induscode/Cargo.toml |
The merged product crate — the two [[bin]] targets (indusr / indusagir) and the feature set. |
crates/induscode/src/bin/main.rs |
The single binary entry: async fn main() -> ExitCode, brand-off-argv[0], the route table. |
crates/induscode/src/lib.rs |
The library barrel re-exporting the 13 former-crate modules as induscode::<module>. |
crates/induscode/src/core/{brand,guardrails,workspace}.rs |
The brand record, the lineage markers, and the single-source VERSION. |
xtask/src/{lineage,framework_via_deps,version}.rs |
The three clean-room / single-source CI gates. |
See also Crate Exports for the module map, CLI flags for the full flag grammar, and the framework's Parity & Lineage for the framework-side clean-room record.
