アーキテクチャ
10クレートで構成されるCargoワークスペース設計: クレートの役割、分離不変条件、そしてCLIブートストラップからマルチレイヤーエージェントのオーケストレーションを経てTUIに至るデータフロー。
stepperはRustで構築されたレイヤード型のCLI/TUI AIコーディングエージェントで、10クレートで構成されるCargoワークスペースとして構造化されています。本ページでは、クレート構成、CLIブートストラップからエージェント実行までのデータフロー、そしてクリーンなアーキテクチャ境界を保証する中核的な分離不変条件を概説します。
クレート概要
ワークスペースには(当初計画された13個のうち)10個のクレートが実装されており、それぞれがシステム内で固有の責務を担います:
| クレート | 役割 |
|---|---|
stepper-protocol | チャネル型とDTO: Action(TUI→core)、AppEvent(core→TUI)、ApprovalRequest(oneshot)。serde、uuid、tokioの同期プリミティブのみに依存します。 |
stepper-tui | インラインビューポートを備えたRatatuiベースのTUI。stepper-protocolのみに依存します(これにratatuiスタックを加えます)。 |
stepper-cli | Clap CLIバイナリ。RealCoreを接続し(デフォルト=TUIエージェント、-pフラグ=ヘッドレス自動承認モード)、auth login・config・initコマンドとMCPサーバーのライフサイクルを処理します。 |
stepper-provider | LLMプロバイダー向けのトレイトと正規化された型: ChatRequest、Message、ContentBlock、ChatEvent、Usage、StopReason、ToolSpec。HTTPを含まず、reqwestもtokio-rtも使用しません。 |
stepper-providers | 具体的なプロバイダーアダプター(Anthropic、OpenAI互換、Responses)、認証(APIキー、OAuth Codex)、reqwest+SSEによるストリーミング、キーリングへのトークン保存。 |
stepper-config | 設定の読み込み: .stepper/の探索、setting.jsonのディープマージ、frontmatterのパース(YAML)、置換エンジン、モデル/プロバイダーの解決、JSONスキーマ検証。 |
stepper-permission | 純粋な権限評価エンジン: deny > ask > allow > modeの優先順位、bashのリダイレクト/コマンドのパース、シンボリックリンクによる脱出の防止、パスの正規化。 |
stepper-tools | Toolトレイトとレジストリ。9個の組み込みツール(read/write/edit/bash/search/grep/todo/web_fetch)、MCPツールのブリッジ、秘密パスの検出、ToolCxによる権限ゲーティング。 |
stepper-mcp | MCP 1.7クライアント(stdio/HTTPトランスポート)、ツールのネームスペーシング、タイムアウト、ローカルツールレジストリとの統合。context7で検証済み。 |
stepper-core | メインオーケストレーター: プロバイダーの解決、AgentLoop(ReActパターン)、マルチステップパイプライン、並列レイヤー実行、セッション/チェックポイント管理、ハンドオフの要約、組み込みスラッシュコマンド。 |
分離不変条件
3つの厳格なアーキテクチャ境界が、cargo metadata解析を用いたCIテスト(crates/stepper-cli/tests/isolation.rs)によって強制されます:
stepper-tuiはstepper-protocolのみに依存します(これにratatuiを加えます)。core、config、providers、HTTPアクセスは一切ありません。reqwestとHTTPはstepper-providers、stepper-tools(web_fetch)、stepper-mcp(HTTPトランスポート)に限定されます。stepper-protocolとstepper-providerトレイトはHTTPを含まず、tokio-rtも使用しません。stepper-protocolはclapを使用しません(チャネル契約にCLIパースが含まれない)。
高レベルのデータフロー
text
stepper-cli main.rs (#[tokio::main])
├─ build_orchestrator(model, mode, cwd)
│ Config::load → build_steps(layer frontmatter+skills) → ensure_provider(convention fallback)
│ McpManager::connect(mcpServers) → register tools to base_tools
│ ConfigProviderResolver + RuleSet + HookHost
├─ channels: mpsc<Action>(TUI→core) + mpsc<AppEvent>(core→TUI) + CancellationToken
├─ stepper-core::spawn_core(orchestrator, session, action_rx, cancel) → event_rx [RealCore]
│ while action:
│ SubmitInput → checkpoint_turn → Orchestrator.run_turn → session append/save
│ SlashCommand → commands::expand(substitution) → run_turn
│ Rewind → restore+turns truncate
│ RunShell → bash tool single-turn execution
│ Orchestrator.run_turn: SessionStart → step layers (sequence or parallel):
│ resolver.resolve(model) → Box<dyn LlmProvider>
│ base_tools.filtered(allow/deny).filter_mcp
│ ToolCx{cwd, project_root, mode, rules, approver=ChannelApprover, cancel}
│ AgentLoop.drive(system, handoff): stream→token/usage emit→compact→tool exec
│ (gate→approver) → result injection → repeat
│ emit: LayerStarted/Finished/ModelChanged/UsageUpdated/ToolCall* → handoff
└─ stepper-tui::run_tui(event_rx, action_tx, init, cancel)
blocking input thread(event::poll/read) → mpsc → select!{input, 33ms tick, AppEvent rx, cancel}
input → (mode-dependent) Action → AppState.apply_action → Effect(Send/CommitToScrollback)
ApprovalRequested(oneshot) → overlay y/a/n → reply.send (resumes agent loop)spawn_fake_core(モック)はウォーキングスケルトンのテスト用にstepper-tuiに残っていますが、CLIはRealCore(stepper-core::spawn_core)のみを使用します。両者は同一のチャネル契約を満たすため、相互に置き換え可能です。主要な型と契約
- **チャネル契約**(固定):
mpsc<Action>+mpsc<AppEvent>+CancellationToken。RealCoreとモックの両方が満たします。 - **
LlmProvider**:chat_stream(req, cancel) → BoxStream<ChatEvent>を実装します。正規化はWireDelta→StreamAccumulator→ChatEventの経路で行われます。 - **
Tool**:spec()/call(args, cx) → ToolResultを実装します。ToolCx.gate(PermissionRequest)が権限評価と承認ゲーティングを強制します。組み込みツールとMCPツールは同じトレイトを使用します。 - **
ProviderResolver**: モデル参照 →Box<dyn LlmProvider>+ModelInfo。ConfigProviderResolverが実装します。 - **
Approver**(tools) → **ChannelApprover**(core): Ask判定時にAppEvent::ApprovalRequested{oneshot}を発生させ → TUIオーバーレイ → ユーザー応答 → エージェントループを再開します。 - **Orchestrator / AgentLoop**:
run_turn→ ステップレイヤー(それぞれ独立したコンテキスト、プロバイダー、ツールを持つ) →AgentLoop.drive(ReActパターン)。ハンドオフ = フリーテキストの要約チェーン。 - **セッションとチェックポイント**:
SessionStore(.stepper/sessions/<id>.json)、Snapshotter(.stepper/checkpoints/<turn>/のファイルコピー)。--resumeはresume_contextをシードし、Rewindは復元・整理・ターンの切り詰めを行います。
高度な機能
- **並列レイヤー**:
parallel: true(および任意のparallel-maxワーカー上限)が設定されたステップは、オーケストレーターが前のレイヤーのassign_tasksリストをワーカーにファンアウトさせます。各ワーカーはサブエージェントです。タスクリストがない場合は逐次実行にフォールバックします。 - **組み込みスラッシュコマンド**:
/help、/clear(セッションのリセット)、/model [provider/model-id](検証 + 最初のステップの切り替え +ModelChangedイベント)、/context(コンテキストウィンドウの要約)。 - **スキル**: レイヤーがfrontmatterに
skillsを宣言すると、SkillToolを通じてモデルがskill { name }を呼び出してレイヤー固有のスキルを利用できます。プログレッシブディスクロージャー: スキル名と説明はシステムプロンプトに提示され、本文はオンデマンドで提供されます。 - **dispatchツール**(C4): モデルは
dispatch(...)を呼び出して、現在のレイヤー以降に並列サブエージェント(ワーカー)を生成できます。オーケストレーターが有効化する必要があり、サブエージェントは再帰できません。 - **プロンプトキャッシュ**:
ChatRequest.cache: boolオプションがAnthropicのプレフィックスキャッシュを有効にし、システムメッセージはcache_control: ephemeralでラップされます。 - **モデル駆動コンパクション**:
compaction.providerが構成されている場合、オーケストレーターはソフトしきい値(コンテキストの0.70)で当該プロバイダーのモデルを用いてメッセージ履歴を要約し、直近6件のメッセージはそのまま保持します。 - **権限ゲーティング**:
ToolCx::gate(request)はモード(Auto/Plan/AcceptEdits)に対してルールを評価します。リダイレクト/$()を含むbashアトムはAskを引き上げ、明示的な承認を要求させます。 - **MCPタイムアウト**: 接続タイムアウト(デフォルト10秒、
STEPPER_MCP_CONNECT_TIMEOUT_MS)は起動時にハングしたサーバーを防ぎます。ツール呼び出しタイムアウト(デフォルト120秒、STEPPER_MCP_TOOL_TIMEOUT_MS)はターン途中のハングを防ぎます。
ビルドとテスト
sh
cargo check --workspace # Type check
cargo clippy --workspace --all-targets # Lint (0 warnings enforced)
cargo test --workspace # full network-less suite (live mcp_live/e2e skip if env unset)
cargo run # Interactive TUI (real tty + API keys required)
cargo run -- -p "..." --model anthropic/claude-x # Headless one-shot (auto-approve)
cargo run -- --resume <session-id> # Resume session
cargo run -- auth login --codex # Codex OAuth login
cargo run -- config --schema | --validate # Settings schema/validation
cargo run -- init # Scaffold .stepper/ (stepper.md + setting.json)