refactor: make mergegate gate-first

This commit is contained in:
林 駿甫 (Shunsuke Hayashi) 2026-04-10 15:03:49 +09:00
parent 29c72fee83
commit 427b99f400
7 changed files with 239 additions and 2244 deletions

1000
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,6 @@
resolver = "2"
members = [
"crates/mergegate-cli",
"crates/mergegate-tui",
"crates/mergegate-core",
]
@ -14,8 +13,8 @@ authors = ["Miyabi Team"]
license = "BUSL-1.1"
repository = "https://github.com/ShunsukeHayashi/mergegate"
homepage = "https://github.com/ShunsukeHayashi/mergegate"
description = "MergeGate - Deterministic task execution and merge workflow for AI-assisted development"
keywords = ["ai", "cli", "tui", "autonomous", "development"]
description = "MergeGate - Engine-agnostic gate CLI for deterministic task execution in AI-assisted development"
keywords = ["ai", "cli", "workflow", "merge", "development"]
categories = ["command-line-utilities", "development-tools"]
[workspace.dependencies]
@ -24,15 +23,6 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-std", "syn
futures = "0.3"
async-trait = "0.1"
# TUI Framework
ratatui = { version = "0.29.0", features = [
"scrolling-regions",
"unstable-backend-writer",
"unstable-rendered-line-info",
"unstable-widget-ref",
] }
crossterm = { version = "0.29.0", features = ["bracketed-paste", "event-stream"] }
# Text Processing
textwrap = "0.16"
unicode-width = "0.2"

View file

@ -1,13 +1,22 @@
# MergeGate
Deterministic execution protocol for AI-assisted development.
Engine-agnostic gate CLI for AI-assisted development.
Execution layer for GitNexus-style code intelligence:
- `GitNexus`: understand the codebase
- `MergeGate`: execute changes safely
Current CLI entrypoints: `miyabi` and `mergegate`
Core product:
- `mergegate gate ...`
- `miyabi gate ...` (compatibility alias)
- `miyabi tui` / `mergegate tui`: terminal assistant
- `miyabi gate` / `mergegate gate`: deterministic task execution, file locks, and PR/merge workflow
MergeGate is designed to sit in front of Claude Code, Codex, Gemini CLI, and other coding agents. The engine can change. The gate protocol should not.
Start with:
```bash
cargo build --release
./target/release/mergegate gate status
./target/release/mergegate gate init
./target/release/mergegate gate guide
```
Start with `cargo build --release`, then run `./target/release/mergegate --help` or `./target/release/mergegate gate guide`.

575
README.md
View file

@ -1,55 +1,68 @@
# MergeGate
MergeGate is a deterministic execution protocol for AI-assisted development.
It is the execution layer for GitNexus-style code intelligence: GitNexus helps agents understand what will break, and MergeGate makes them execute that work in a safe, verifiable order.
MergeGate is an engine-agnostic gate CLI for AI-assisted development.
It does not need to be your coding agent, your chat runtime, or your terminal UI. Its job is simpler and more durable:
- register work
- record impact
- lock files
- assign execution
- track branch and PR state
- verify completion
In short:
- `GitNexus`: understand the codebase
- `MergeGate`: execute changes safely
The project currently supports both `miyabi` and `mergegate` as CLI entrypoints. `miyabi` remains the legacy/default command, and `mergegate` is the product-aligned alias.
MergeGate is designed to sit in front of coding agents such as Claude Code, Codex, and Gemini CLI. The execution engine can change. The gate protocol should not.
This repository has two common entry points:
## What This Project Is
- `miyabi tui` / `mergegate tui`: interactive terminal assistant
- `miyabi gate` / `mergegate gate`: deterministic task execution and file-lock workflow for repo work
MergeGate is a repo workflow tool for deterministic execution.
If you arrived here because of deterministic execution, task locks, PR gates, or agent-safe repo work, start with `mergegate gate` or `miyabi gate`, not the TUI.
The core product is:
- `miyabi gate ...`
- `mergegate gate ...`
The binary still supports both `miyabi` and `mergegate` entrypoints today. `mergegate` is the product-aligned command. `miyabi` remains as a compatibility alias.
## 60-Second Setup
```bash
# 1. Clone and build
git clone https://github.com/ShunsukeHayashi/mergegate.git
cd mergegate
cargo build --release
# 2. Set API key
export ANTHROPIC_API_KEY="sk-ant-..."
# 3. Run
./target/release/mergegate tui
./target/release/mergegate gate status
./target/release/mergegate gate init
./target/release/mergegate gate guide
```
That's it. Start with the TUI, or jump straight into `mergegate gate` for repo workflow control.
---
No API key is required for gate workflow.
## Why MergeGate
- **Execution layer for code intelligence** - Pair blast-radius understanding with deterministic execution, not chat-only automation
- **Deterministic task execution** - Register, analyze, lock, review, and merge in a verifiable order
- **Repo-safe agent workflow** - File locks and gate checks reduce accidental overlap and unsafe edits
- **Interactive TUI** - Terminal assistant for chat, prompts, and agent execution
- **Agent mode** - Autonomous execution with tool approval
- **Session management** - Persist and resume conversations
- **Configurable** - TOML-based configuration
- **Engine agnostic**: Use Claude Code, Codex, Gemini CLI, or another agent runtime
- **Deterministic execution**: Register, analyze, lock, implement, review, and merge in a verifiable order
- **Repo-safe coordination**: Reduce accidental overlap with file locks and explicit task ownership
- **Execution ledger**: Keep branch, PR, merge, and completion state tied to actual work
- **Protocol over chat**: The product is the workflow gate, not a built-in assistant
## Positioning
MergeGate was designed for teams that already believe code understanding is not enough.
Knowing the blast radius of a change is only half of the problem. Agents also need a protocol for when work starts, which files are locked, what must be recorded before editing, and what counts as done.
MergeGate exists because understanding code is not enough.
Agents also need a protocol for:
- when a task starts
- which files are locked
- what impact was recorded
- who is implementing
- how branch and PR state are attached
- what counts as done
That is the role split:
@ -63,7 +76,6 @@ If GitNexus answers "what is this change connected to?", MergeGate answers "how
### Prerequisites
- Rust 1.70+
- Anthropic API key
### Build from Source
@ -77,75 +89,34 @@ Binary will be at `target/release/miyabi` and `target/release/mergegate`.
## Quick Start
Choose the path that matches what you want to do.
### A. Use MergeGate as a terminal assistant
Run the TUI after configuring your API key.
#### 1. Generate Config
### 1. Check repo status
```bash
./target/release/miyabi init
./target/release/mergegate gate status
```
This creates `~/.miyabi/config.toml`.
If you see `tasks: 0`, the ledger exists and is simply empty.
#### 2. Set API Key
Edit `~/.miyabi/config.toml`:
```toml
[api]
api_key = "sk-ant-..."
```
Or use environment variable:
### 2. Initialize gate ledger if needed
```bash
export ANTHROPIC_API_KEY="sk-ant-..."
./target/release/mergegate gate init
```
#### 3. Launch TUI
This creates `project_memory/tasks.json`.
### 3. Read the built-in workflow guide
```bash
./target/release/miyabi tui
# or simply
./target/release/miyabi
./target/release/mergegate gate guide
```
### B. Use MergeGate as a task execution gate
If you are working inside a repository and want task registration, impact tracking, file locks, and PR/merge recording, use `mergegate gate` or `miyabi gate`.
#### 1. Check whether the repo is already initialized
### 4. Start a task
```bash
./target/release/miyabi gate status
```
If you see `tasks: 0`, that means the ledger exists but is currently empty.
#### 2. Initialize MergeGate for this repo if needed
```bash
./target/release/miyabi gate init
```
This creates `project_memory/tasks.json` and prepares the repo for gate-managed work.
#### 3. Read the built-in workflow guide
```bash
./target/release/miyabi gate guide
```
#### 4. Start a task
```bash
./target/release/miyabi gate register --issue 123 --title "Fix login redirect"
./target/release/miyabi gate impact issue-123 --risk medium --symbols 3
./target/release/miyabi gate assign issue-123 --agent codex --node macbook --files "src/auth.rs"
./target/release/mergegate gate register --issue 123 --title "Fix login redirect"
./target/release/mergegate gate impact issue-123 --risk medium --symbols 3
./target/release/mergegate gate assign issue-123 --agent codex --node macbook --files "src/auth.rs"
```
Typical flow:
@ -153,427 +124,83 @@ Typical flow:
1. `register`
2. `impact`
3. `assign`
4. implement
4. implement with your coding agent of choice
5. `branch`
6. `pr`
7. `merge` or `manual-complete`
## Usage
`MergeGate` is the product and documentation name.
Both `miyabi` and `mergegate` work today.
```bash
mergegate gate status
mergegate gate init
mergegate gate guide
mergegate gate register --issue 123 --title "Fix login redirect"
mergegate gate impact issue-123 --risk medium --symbols 3
mergegate gate assign issue-123 --agent codex --node macbook --files "src/auth.rs"
mergegate gate branch issue-123 codex/fix-login-redirect
mergegate gate pr issue-123 456
mergegate gate merge issue-123 <sha>
```
### CLI Commands
Compatibility alias:
```bash
miyabi # Start TUI (default)
miyabi tui # Start TUI explicitly
miyabi status # Show status
miyabi init # Generate default config
miyabi sessions # List sessions
miyabi sessions -d <id> # Delete session
miyabi sessions -e <id> # Export session to JSON
miyabi version # Show version info
miyabi agent <prompt> # Run autonomous agent
miyabi gate status # Show task ledger status
miyabi gate init # Initialize MergeGate in the current repo
miyabi gate guide # Show the full gate workflow guide
mergegate gate status # Same gate workflow with product-aligned command
mergegate tui # Same TUI with product-aligned command
miyabi gate status
```
### CLI Options
## Core Commands
```bash
miyabi --model claude-sonnet-4-5-20250929 # Override model
miyabi --max-tokens 16384 # Override max tokens
miyabi --thinking # Enable extended thinking
miyabi --config /path/to/config.toml # Custom config
miyabi --session <id> # Resume session
mergegate gate status
mergegate gate init
mergegate gate guide
mergegate gate register --issue 123 --title "Fix login redirect"
mergegate gate assign issue-123 --agent codex --node macbook --files "src/auth.rs"
mergegate gate impact issue-123 --risk medium --symbols 3
mergegate gate branch issue-123 codex/fix-login-redirect
mergegate gate pr issue-123 456
mergegate gate merge issue-123 <sha>
mergegate gate manual-complete issue-123 --reason "docs-only change" --operator shunsuke
mergegate gate locks
mergegate gate dispatchable
mergegate gate dag
```
### TUI Keybindings
## Product Direction
| Key | Action |
|-----|--------|
| `Enter` | Send message |
| `Ctrl+P` | Command palette |
| `F1` | Help |
| `Esc` | Close overlay / Cancel |
| `Ctrl+C` | Quit |
| `j/k` | Scroll down/up |
| `Page Up/Down` | Page scroll |
MergeGate is intentionally moving away from:
## Configuration
- built-in TUI as the product center
- built-in agent runtime as the product center
- vendor-specific backend identity
Config file: `~/.miyabi/config.toml`
The durable surface is the gate CLI.
```toml
[api]
api_key = "sk-ant-..." # Anthropic API key
model = "claude-sonnet-4-5-20250929" # Model to use
max_tokens = 8192 # Max response tokens
thinking = false # Extended thinking
system_prompt = "You are a helpful assistant" # System prompt
timeout_secs = 120 # Request timeout
max_retries = 3 # Retry attempts
## Workspace Layout
[ui]
show_sidebar = false # Show sidebar
show_status_bar = true # Show status bar
show_breadcrumb = true # Show breadcrumb
theme = "tokyo-night" # Color theme
vim_mode = false # Vim keybindings
show_line_numbers = true # Line numbers in code
[session]
auto_save = true # Auto-save sessions
auto_save_interval = 30 # Save interval (seconds)
max_sessions = 100 # Max stored sessions
[tools]
enable_bash = true # Enable bash tool
enable_file_tools = true # Enable file read/write
enable_search_tools = true # Enable glob/grep
auto_approve_low_risk = false # Auto-approve safe tools
bash_timeout = 120 # Bash command timeout
```text
crates/
mergegate-cli/ # CLI entrypoint
mergegate-core/ # Gate protocol, store, locks, workflow logic
project_memory/
tasks.json # Execution ledger
skills/
mergegate-cli/
mergegate-ops/
```
### Environment Variables
Override config with environment variables:
```bash
ANTHROPIC_API_KEY=... # API key
MIYABI_MODEL=... # Model name
MIYABI_MAX_TOKENS=... # Max tokens
MIYABI_THINKING=true # Enable thinking
```
## Agent Mode
Run autonomous tasks with tool execution:
```bash
# Basic usage
miyabi agent "Create a hello world Python script"
# With options
miyabi agent "Refactor the utils module" \
--max-iterations 20 \
--auto-approve \
--system "You are an expert Rust developer"
```
Agent mode features:
- Autonomous tool execution (bash, file operations, search)
- Approval workflow for risky operations
- Iteration limits for safety
- JSON output format option
## Core Library Features
The `mergegate-core` crate provides powerful utilities for building AI applications.
### Git Utilities
```rust
use miyabi_core::{find_git_root, get_current_branch, is_in_git_repo};
// Find repository root
let root = find_git_root()?;
// Get current branch
let branch = get_current_branch(&root)?;
// Check if in git repo
if is_in_git_repo(&path) {
println!("In a git repository");
}
```
### Logger System
```rust
use miyabi_core::{init_logger, LogLevel, LogFormat};
// Initialize with defaults
init_logger();
// Or with custom config
use miyabi_core::LoggerConfig;
let config = LoggerConfig {
level: LogLevel::Debug,
format: LogFormat::Json,
..Default::default()
};
init_logger_with_config(config);
```
### Retry with Backoff
```rust
use miyabi_core::{retry_with_backoff, BackoffRetryConfig};
let config = BackoffRetryConfig::default();
let result = retry_with_backoff(config, || async {
// Your operation that might fail
make_api_call().await
}).await?;
```
### Circuit Breaker
```rust
use miyabi_core::{CircuitBreaker, CircuitState};
let breaker = CircuitBreaker::default();
let result = breaker.call(|| {
Box::pin(async {
// Your operation
Ok::<_, std::io::Error>(())
})
}).await;
// Check state
match breaker.state().await {
CircuitState::Closed => println!("Normal operation"),
CircuitState::Open => println!("Circuit open - blocking calls"),
CircuitState::HalfOpen => println!("Testing recovery"),
}
```
### Rules System (.miyabirules)
Support for project-specific rules via `.miyabirules` files:
```yaml
# .miyabirules
version: 1
rules:
- name: "no-unwrap"
pattern: ".unwrap()"
suggestion: "Use ? operator or proper error handling"
file_extensions: ["rs"]
severity: "warning"
agent_preferences:
codegen:
style: "functional"
error_handling: "result"
```
```rust
use miyabi_core::{RulesLoader, MiyabiRules};
let loader = RulesLoader::new(project_root);
let rules = loader.load_or_default()?;
// Get rules for a file
let applicable = rules.rules_for_file(Path::new("src/main.rs"));
```
### Cache System
```rust
use miyabi_core::{create_llm_cache, create_api_cache, LLMCacheKey};
// LLM response cache (1 hour TTL)
let cache = create_llm_cache();
let key = LLMCacheKey::new("prompt", "model", Some(0.7));
cache.insert(key.clone(), "response".to_string()).await;
let cached = cache.get(&key).await;
// API cache (30 min TTL)
let api_cache = create_api_cache();
```
### Plugin System
```rust
use miyabi_core::{Plugin, PluginManager, PluginMetadata, PluginContext, PluginResult};
use anyhow::Result;
struct MyPlugin;
impl Plugin for MyPlugin {
fn metadata(&self) -> PluginMetadata {
PluginMetadata {
name: "my-plugin".to_string(),
version: "1.0.0".to_string(),
description: Some("My custom plugin".to_string()),
author: None,
}
}
fn init(&mut self) -> Result<()> {
Ok(())
}
fn execute(&self, ctx: &PluginContext) -> Result<PluginResult> {
Ok(PluginResult {
success: true,
message: Some("Done".to_string()),
data: None,
})
}
}
// Register and execute
let manager = PluginManager::new();
manager.register(Box::new(MyPlugin))?;
let result = manager.execute("my-plugin", &PluginContext::default())?;
```
### Feature Flags
```rust
use miyabi_core::{FeatureFlagManager, FeatureFlag};
let flags = FeatureFlagManager::new();
// Simple flag
flags.set_flag("new_feature", true);
if flags.is_enabled("new_feature") {
// Use new feature
}
// With rollout percentage
flags.set_flag_with_options(
"beta_feature",
true,
Some("Beta testing".to_string()),
Some(0.5), // 50% rollout
);
// Load from config
use std::collections::HashMap;
let mut config = HashMap::new();
config.insert("flag1".to_string(), true);
flags.load_from_map(config);
```
## Project Structure
```
mergegate/
├── crates/
│ ├── mergegate-cli/ # CLI entry point
│ ├── mergegate-core/ # Core library
│ │ ├── agent.rs # Agent system
│ │ ├── anthropic.rs # Anthropic API client
│ │ ├── cache.rs # TTL cache system
│ │ ├── config.rs # Configuration
│ │ ├── error_policy.rs # Circuit breaker
│ │ ├── feature_flags.rs # Feature flag management
│ │ ├── git.rs # Git utilities
│ │ ├── logger.rs # Logging system
│ │ ├── plugin.rs # Plugin system
│ │ ├── retry.rs # Retry with backoff
│ │ ├── rules.rs # .miyabirules support
│ │ ├── session.rs # Session management
│ │ ├── tools.rs # Built-in tools
│ │ └── ...
│ └── mergegate-tui/ # TUI implementation
│ ├── app.rs # Main application
│ ├── views.rs # UI views
│ └── ...
├── .miyabi/ # Local config
├── Cargo.toml # Workspace config
└── README.md
```
## Development
### Build
```bash
cargo build # Debug build
cargo build --release # Release build
```
### Test
```bash
cargo test --all # Run all tests
```
### Lint
```bash
cargo clippy --all-targets -- -D warnings
cargo fmt --all --check
```
## Models
Supported models:
- `claude-sonnet-4-5-20250929` (default)
- `claude-haiku-4-5-20251001`
- `claude-sonnet-4-20250514`
## Troubleshooting
### Common Issues
### `gate status` says `tasks: 0`
#### "API key not found"
```bash
# Check if environment variable is set
echo $ANTHROPIC_API_KEY
That is not an error. It means the ledger exists but no tasks have been registered yet.
# Or add to config file
vim ~/.miyabi/config.toml
# [api]
# api_key = "sk-ant-..."
```
### `gate init` says the project is already initialized
#### "Device not configured" error on TUI start
This usually means the terminal doesn't support the required features. Try:
```bash
# Use a different terminal emulator (iTerm2, Alacritty, etc.)
# Or run with explicit terminal type
TERM=xterm-256color ./target/release/miyabi tui
```
That is usually safe. It means `project_memory/tasks.json` already exists.
#### Build errors
```bash
# Make sure Rust is up to date
rustup update
### I want to use another coding agent
# Clean and rebuild
cargo clean
cargo build --release
```
That is the intended model. MergeGate is the gate, not the engine.
#### Session not saving
```bash
# Check permissions on sessions directory
ls -la ~/.miyabi/sessions/
# Create if missing
mkdir -p ~/.miyabi/sessions
```
### Debug Mode
For verbose logging:
```bash
RUST_LOG=debug ./target/release/miyabi tui
```
### Getting Help
- Check `miyabi --help` for CLI options
- Press `F1` in TUI for keybindings
- Open an issue: https://github.com/ShunsukeHayashi/mergegate/issues
## License
MIT
---
Built with Rust, Ratatui, and Claude API.

View file

@ -7,7 +7,7 @@ authors.workspace = true
license.workspace = true
repository.workspace = true
homepage.workspace = true
description = "MergeGate CLI - Main command-line interface for deterministic task execution and agent-assisted development"
description = "MergeGate CLI - Engine-agnostic gate workflow for deterministic task execution"
[[bin]]
name = "miyabi"
@ -20,15 +20,10 @@ path = "src/bin/mergegate.rs"
[dependencies]
# Workspace crates
miyabi-core = { package = "mergegate-core", path = "../mergegate-core" }
miyabi-tui = { package = "mergegate-tui", path = "../mergegate-tui" }
# CLI
clap = { workspace = true }
# TUI Framework
ratatui = { workspace = true }
crossterm = { workspace = true }
# Async Runtime
tokio = { workspace = true }

View file

@ -62,36 +62,14 @@ fn agent_guide() -> String {
}
#[derive(Parser)]
#[command(author, version, about = "MergeGate - Deterministic task execution and merge workflow for AI-assisted development", long_about = None)]
#[command(author, version, about = "MergeGate - Engine-agnostic gate CLI for AI-assisted development", long_about = None)]
struct Cli {
/// Model to use (overrides config)
#[arg(short, long)]
model: Option<String>,
/// Maximum tokens for responses (overrides config)
#[arg(long)]
max_tokens: Option<u32>,
/// Enable Extended Thinking (Claude 4.5+)
#[arg(long)]
thinking: bool,
/// Path to config file
#[arg(short, long)]
config: Option<PathBuf>,
/// Session ID to load on startup
#[arg(short, long)]
session: Option<String>,
#[command(subcommand)]
command: Option<Commands>,
}
#[derive(Subcommand)]
enum Commands {
/// Start the TUI interface
Tui,
/// Show status
Status,
/// Generate default config file
@ -116,23 +94,6 @@ enum Commands {
#[arg(short, long)]
verbose: bool,
},
/// Run agent with a prompt (autonomous execution)
Agent {
/// The prompt to execute
prompt: String,
/// Maximum iterations (default: 10)
#[arg(long, default_value = "10")]
max_iterations: usize,
/// Auto-approve all tool executions
#[arg(long)]
auto_approve: bool,
/// Output format: text or json
#[arg(long, default_value = "text")]
format: String,
/// System prompt for the agent
#[arg(long)]
system: Option<String>,
},
#[command(
about = "Deterministic Task Protocol gate controls",
long_about = "Run the gate init command to set up a new project.\n\nDeterministic Task Protocol gate controls"
@ -452,78 +413,35 @@ async fn main() -> anyhow::Result<()> {
let cli = Cli::from_arg_matches(&matches).unwrap_or_else(|error| error.exit());
match cli.command {
Some(Commands::Tui) | None => {
// Run TUI
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
terminal::{
disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen,
},
};
use miyabi_core::config::Config;
use miyabi_tui::App;
use ratatui::prelude::*;
use std::io;
// Load config (from custom path or default)
let mut config = if let Some(config_path) = &cli.config {
Config::load_from(config_path)?
} else {
Config::load().unwrap_or_default()
};
// Apply CLI overrides
if let Some(model) = &cli.model {
config.api.model = model.clone();
}
if let Some(max_tokens) = cli.max_tokens {
config.api.max_tokens = max_tokens;
}
if cli.thinking {
config.api.thinking = true;
}
enable_raw_mode()?;
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let mut app = App::with_config(config);
// Load session if specified
if let Some(session_id) = &cli.session {
if let Err(e) = app.load_session(session_id) {
eprintln!("Warning: Failed to load session {}: {}", session_id, e);
}
}
let res = app.run(&mut terminal).await;
disable_raw_mode()?;
execute!(
terminal.backend_mut(),
LeaveAlternateScreen,
DisableMouseCapture
)?;
terminal.show_cursor()?;
if let Err(err) = res {
eprintln!("Error: {}", err);
}
None => {
let mut command = Cli::command();
command = command.name(current_binary_name());
command.print_help()?;
println!();
println!();
println!("Start here:");
println!(" {}", gate_command("status"));
println!(" {}", gate_command("init"));
println!(" {}", gate_command("guide"));
}
Some(Commands::Status) => {
use miyabi_core::config::Config;
let config = Config::load().unwrap_or_default();
println!("Miyabi Status: Ready");
println!("MergeGate Status: Ready");
println!();
println!("Binary: {}", current_binary_name());
println!("Config: {}", Config::default_path().display());
println!("Sessions: {}", config.sessions_dir().display());
println!("Model: {}", config.api.model);
println!();
println!("Core workflow:");
println!(" {}", gate_command("status"));
println!(" {}", gate_command("init"));
println!(" {}", gate_command("guide"));
println!();
println!("Direct runtime config remains on disk for compatibility.");
println!("Model: {}", config.api.model);
println!("Sessions: {}", config.sessions_dir().display());
// Load and show rules info
let cwd = std::env::current_dir().unwrap_or_default();
@ -766,152 +684,6 @@ async fn main() -> anyhow::Result<()> {
}
}
}
Some(Commands::Agent {
prompt,
max_iterations,
auto_approve,
format,
system,
}) => {
use miyabi_core::{
config::Config, Agent, AgentConfig, AgentEvent, AnthropicClient, ExecutorRegistry,
};
use tokio::sync::mpsc;
// Load config
let mut config = if let Some(config_path) = &cli.config {
Config::load_from(config_path)?
} else {
Config::load().unwrap_or_default()
};
// Apply CLI overrides
if let Some(model) = &cli.model {
config.api.model = model.clone();
}
if let Some(max_tokens) = cli.max_tokens {
config.api.max_tokens = max_tokens;
}
if cli.thinking {
config.api.thinking = true;
}
// Get API key
let api_key = config
.api
.api_key
.clone()
.or_else(|| std::env::var("ANTHROPIC_API_KEY").ok())
.ok_or_else(|| {
anyhow::anyhow!("No API key found. Set ANTHROPIC_API_KEY or add to config.")
})?;
// Create client
let client = AnthropicClient::new(api_key)?
.with_model(&config.api.model)
.with_max_tokens(config.api.max_tokens)
.with_thinking(config.api.thinking);
// Create executor registry with standard tools
let registry = ExecutorRegistry::with_standard_tools();
// Configure agent
let agent_config = AgentConfig {
max_iterations,
max_tokens_per_turn: config.api.max_tokens,
require_approval: !auto_approve,
auto_approve_patterns: if auto_approve {
vec![
"read".to_string(),
"glob".to_string(),
"grep".to_string(),
"write".to_string(),
"edit".to_string(),
"bash".to_string(),
]
} else {
vec!["read".to_string(), "glob".to_string(), "grep".to_string()]
},
..Default::default()
};
// Create agent
let mut agent = Agent::new(client, registry).with_config(agent_config);
// Set system prompt
if let Some(sys) = system {
agent = agent.with_system_prompt(sys);
} else if let Some(sys) = config.api.system_prompt {
agent = agent.with_system_prompt(sys);
}
// Create event channel for progress
let (tx, mut rx) = mpsc::channel(100);
let agent = agent.with_event_channel(tx);
// Spawn agent execution
let agent_handle = tokio::spawn(async move { agent.run(&prompt).await });
// Process events
while let Some(event) = rx.recv().await {
match &event {
AgentEvent::Started { prompt } => {
if format != "json" {
eprintln!("🚀 Agent started with prompt: {}", truncate_str(prompt, 50));
}
}
AgentEvent::Thinking { iteration } => {
if format != "json" {
eprintln!("💭 Iteration {}", iteration + 1);
}
}
AgentEvent::ToolDetected { name, .. } => {
if format != "json" {
eprintln!("🔧 Tool detected: {}", name);
}
}
AgentEvent::ToolExecuting { name, .. } => {
if format != "json" {
eprintln!("⚡ Executing: {}", name);
}
}
AgentEvent::ToolCompleted { name, .. } => {
if format != "json" {
eprintln!("✅ Completed: {}", name);
}
}
AgentEvent::ToolFailed { name, error } => {
if format != "json" {
eprintln!("❌ Failed {}: {}", name, error);
}
}
AgentEvent::Completed { result } => {
if format == "json" {
println!("{}", serde_json::to_string_pretty(&result)?);
} else {
println!("\n{}", result.output);
eprintln!(
"\n📊 Stats: {} iterations, {} tool calls, {} tokens",
result.iterations, result.tool_calls, result.total_tokens
);
}
}
AgentEvent::Failed { error } => {
eprintln!("❌ Agent failed: {}", error);
}
_ => {}
}
}
// Wait for agent to complete
match agent_handle.await? {
Ok(_) => {}
Err(e) => {
eprintln!("Agent error: {}", e);
std::process::exit(1);
}
}
}
Some(Commands::Gate {
format,
emit_event,

View file

@ -1,578 +1,172 @@
# MergeGate ユーザーマニュアル
**Version**: 0.1.0
**Last Updated**: 2025-11-23
---
## 目次
1. [はじめに](#はじめに)
2. [インストール](#インストール)
3. [初期設定](#初期設定)
4. [基本的な使い方](#基本的な使い方)
5. [TUIモード](#tuiモード)
6. [Agentモード](#agentモード)
7. [セッション管理](#セッション管理)
8. [設定ファイル](#設定ファイル)
9. [プロジェクトルール](#プロジェクトルール)
10. [トラブルシューティング](#トラブルシューティング)
---
**Last Updated**: 2026-04-10
## はじめに
MergeGate は、AI-assisted development 向けの deterministic task execution and merge workflow です。CLI では `miyabi``mergegate` の両方を使え、対話型の TUI と `gate` ベースの repo workflow を提供します。
MergeGate は、AI-assisted development 向けの engine-agnostic gate CLI です。
設計思想はシンプルです。
このツールはエージェント本体ではありません。TUI や direct backend を前提にせず、repo 作業を安全に進めるための protocol を提供します。
役割は次の通りです。
- task を登録する
- impact を記録する
- file lock を取る
- branch / PR / merge 状態を結び付ける
- completion を記録する
設計思想:
- `GitNexus`: コードベースを理解する
- `MergeGate`: 変更を安全に実行する
影響範囲が分かるだけでは、AI エージェントは安全に開発できません。どの task を登録し、どのファイルを lock し、どの順序で branch / PR / merge まで進めるかを protocol として固定するのが MergeGate の役割です。
## 何をするツールか
### 主な機能
MergeGate の中心機能は `gate` です。
- **TUIモード**: 美しいターミナルUIでの対話
- **Agentモード**: ファイル操作やコマンド実行を含む自律実行
- **セッション管理**: 会話履歴の保存・再開
- **プロジェクトルール**: .miyabirulesによるカスタムルール
```bash
mergegate gate ...
```
---
または互換 alias として:
```bash
miyabi gate ...
```
実行エンジンは Claude Code、Codex、Gemini CLI など、どれでも構いません。MergeGate はその前後で repo workflow を制御します。
## インストール
### 必要要件
- Rust 1.70以上
- Anthropic APIキー
### ビルド手順
### ビルド
```bash
# 1. リポジトリをクローン
git clone https://github.com/ShunsukeHayashi/mergegate.git
cd mergegate
# 2. リリースビルド
cargo build --release
# 3. バイナリの確認
ls -la target/release/miyabi target/release/mergegate
```
### パスを通す(オプション)
## 最初にやること
### 1. repo の状態確認
```bash
# ~/.bashrc または ~/.zshrc に追加
export PATH="$PATH:/path/to/mergegate/target/release"
# 設定を反映
source ~/.bashrc # または source ~/.zshrc
./target/release/mergegate gate status
```
---
`tasks: 0` は正常です。ledger はあるが task がまだ無い状態です。
## 初期設定
### 1. 設定ファイルの生成
### 2. 初期化
```bash
./target/release/miyabi init
./target/release/mergegate gate init
```
これにより `~/.miyabi/config.toml` が作成されます。
これ`project_memory/tasks.json` が作成されます。
### 2. APIキーの設定
**方法1: 環境変数(推奨)**
```bash
export ANTHROPIC_API_KEY="sk-ant-api03-..."
```
永続化する場合は `~/.bashrc``~/.zshrc` に追加:
```bash
echo 'export ANTHROPIC_API_KEY="sk-ant-api03-..."' >> ~/.zshrc
```
**方法2: 設定ファイル**
```bash
vim ~/.miyabi/config.toml
```
```toml
[api]
api_key = "sk-ant-api03-..."
```
### 3. 設定の確認
### 3. ガイド確認
```bash
./target/release/miyabi status
./target/release/mergegate gate guide
```
出力例:
```
Miyabi Status: Ready
## 基本フロー
Config: /Users/you/.miyabi/config.toml
Sessions: /Users/you/.miyabi/sessions
Model: claude-sonnet-4-5-20250929
Rules: 0 rules loaded
```bash
./target/release/mergegate gate register --issue 123 --title "Fix login redirect"
./target/release/mergegate gate impact issue-123 --risk medium --symbols 3
./target/release/mergegate gate assign issue-123 --agent codex --node macbook --files "src/auth.rs"
./target/release/mergegate gate branch issue-123 codex/fix-login-redirect
./target/release/mergegate gate pr issue-123 456
./target/release/mergegate gate merge issue-123 <sha>
```
---
典型的な順番:
## 基本的な使い方
1. `register`
2. `impact`
3. `assign`
4. 実装
5. `branch`
6. `pr`
7. `merge` または `manual-complete`
### コマンド一覧
## コマンド一覧
| コマンド | 説明 |
|---------|------|
| `miyabi tui` | TUIモードを起動 |
| `miyabi agent <prompt>` | Agentモードで実行 |
| `miyabi status` | ステータス表示 |
| `miyabi version` | バージョン情報 |
| `miyabi init` | 設定ファイル生成 |
| `miyabi sessions` | セッション一覧 |
| `miyabi rules` | プロジェクトルール表示 |
| `mergegate gate status` | ledger 全体または task 状態を表示 |
| `mergegate gate init` | ledger を初期化 |
| `mergegate gate guide` | workflow ガイドを表示 |
| `mergegate gate register` | task を登録 |
| `mergegate gate impact` | impact を記録 |
| `mergegate gate assign` | task を割り当てて lock を取得 |
| `mergegate gate branch` | branch を記録 |
| `mergegate gate pr` | PR 番号を記録 |
| `mergegate gate merge` | merge を記録 |
| `mergegate gate manual-complete` | 手動完了を記録 |
| `mergegate gate locks` | active lock を表示 |
| `mergegate gate dispatchable` | 今着手できる task を表示 |
| `mergegate gate dag` | 依存順序を表示 |
### グローバルオプション
## 実行エンジンとの関係
| オプション | 説明 |
|-----------|------|
| `-m, --model <MODEL>` | 使用するモデルを指定 |
| `--max-tokens <N>` | 最大トークン数 |
| `--thinking` | Extended Thinking有効化 |
| `-c, --config <PATH>` | 設定ファイルパス |
| `-s, --session <ID>` | セッションID指定 |
MergeGate は coding agent を置き換えるものではありません。
### 使用例
想定している使い方:
```bash
# バージョン確認
./target/release/miyabi version
- Claude Code が実装する
- Codex が実装する
- Gemini CLI が実装する
- MergeGate が task / lock / merge discipline を管理する
# ステータス確認
./target/release/miyabi status
つまり:
# モデル指定でTUI起動
./target/release/miyabi tui --model claude-sonnet-4-5-20250929
- エージェントは交換可能
- gate protocol は固定
# Extended Thinking有効でTUI起動
./target/release/miyabi tui --thinking
```
## よくある質問
---
### API キーは必要ですか
## TUIモード
`gate` workflow だけなら不要です。
### 起動
### TUI は必要ですか
```bash
./target/release/miyabi tui
```
不要です。MergeGate の本体ではありません。
### キーバインド
### built-in backend は必要ですか
#### 基本操作
不要です。MergeGate の本体ではありません。
| キー | 動作 |
|------|------|
| `Enter` | メッセージ送信 |
| `Ctrl+C` | 終了 |
| `Esc` | オーバーレイを閉じる / キャンセル |
| `F1` | ヘルプ表示 |
### `miyabi``mergegate` のどちらを使えばいいですか
#### ナビゲーション
| キー | 動作 |
|------|------|
| `j` / `↓` | 下にスクロール |
| `k` / `↑` | 上にスクロール |
| `Page Down` | ページ下へ |
| `Page Up` | ページ上へ |
| `g` | 先頭へ |
| `G` | 末尾へ |
#### コマンド
| キー | 動作 |
|------|------|
| `Ctrl+P` | コマンドパレット |
| `Ctrl+N` | 新規セッション |
| `Ctrl+O` | セッションを開く |
| `Ctrl+S` | セッション保存 |
### Vimモード
設定で有効化できます:
```toml
[ui]
vim_mode = true
```
有効にすると、テキスト入力でVimキーバインドが使用可能になります。
---
## Agentモード
Agentモードは、プロンプトに基づいて自律的にタスクを実行します。
### 基本使用
```bash
./target/release/miyabi agent "タスクの説明"
```
### オプション
| オプション | 説明 | デフォルト |
|-----------|------|-----------|
| `--max-iterations <N>` | 最大イテレーション数 | 10 |
| `--auto-approve` | ツール実行を自動承認 | false |
| `--format <FORMAT>` | 出力形式 (text/json) | text |
| `--system <PROMPT>` | システムプロンプト | なし |
### 使用例
```bash
# 基本的な実行
./target/release/miyabi agent "Create a Python script that prints hello world"
# 自動承認モード(注意して使用)
./target/release/miyabi agent --auto-approve "List all .rs files in src/"
# イテレーション数を増やす
./target/release/miyabi agent --max-iterations 20 "Refactor the utils module"
# JSON出力
./target/release/miyabi agent --format json "Show current directory"
# カスタムシステムプロンプト
./target/release/miyabi agent --system "You are an expert Rust developer" "Review main.rs"
```
### 利用可能なツール
Agentモードでは以下のツールが使用可能です
- **Bash**: シェルコマンドの実行
- **Read**: ファイルの読み取り
- **Write**: ファイルの書き込み
- **Edit**: ファイルの編集
- **Glob**: ファイルパターン検索
- **Grep**: テキスト検索
### セキュリティ
- デフォルトでは危険な操作は承認を求められます
- `--auto-approve` は信頼できるタスクのみに使用してください
- `--max-iterations` で無限ループを防止
---
## セッション管理
### セッション一覧の表示
```bash
./target/release/miyabi sessions
```
出力例:
```
ID Title Messages Tokens Updated
------------------------------------------------------------------------------------------
133b91bf-99c9-4097-974c-c60c9abd2495 test 6 246 2025-11-22 14:36
19af5958-183f-4b39-be2e-b14b0809181c test 2 21 2025-11-22 14:03
```
### セッションの操作
```bash
# セッションを削除
./target/release/miyabi sessions -d <session-id>
# JSONにエクスポート
./target/release/miyabi sessions -e <session-id>
# Markdownにエクスポート
./target/release/miyabi sessions -m <session-id>
```
### セッションの再開
```bash
# 特定のセッションからTUIを起動
./target/release/miyabi tui -s <session-id>
```
### 保存場所
セッションは `~/.miyabi/sessions/` に保存されます。
---
## 設定ファイル
### 設定ファイルの場所
```
~/.miyabi/config.toml
```
### 完全な設定例
```toml
[api]
# Anthropic APIキー環境変数ANTHROPIC_API_KEYでも設定可能
api_key = "sk-ant-api03-..."
# 使用するモデル
model = "claude-sonnet-4-5-20250929"
# 最大トークン数
max_tokens = 8192
# Extended ThinkingClaude 4.5+
thinking = false
# システムプロンプト
system_prompt = "You are a helpful AI assistant."
# リクエストタイムアウト(秒)
timeout_secs = 120
# 最大リトライ回数
max_retries = 3
[ui]
# サイドバー表示
show_sidebar = false
# ステータスバー表示
show_status_bar = true
# パンくずリスト表示
show_breadcrumb = true
# カラーテーマ
theme = "tokyo-night"
# Vimモード
vim_mode = false
# コードブロックの行番号
show_line_numbers = true
[session]
# セッションの自動保存
auto_save = true
# 自動保存間隔(秒)
auto_save_interval = 30
# 最大セッション数
max_sessions = 100
[tools]
# Bashツールを有効化
enable_bash = true
# ファイルツールread/write/editを有効化
enable_file_tools = true
# 検索ツールglob/grepを有効化
enable_search_tools = true
# 低リスクツールの自動承認
auto_approve_low_risk = false
# Bashコマンドのタイムアウト
bash_timeout = 120
```
### 環境変数による上書き
| 環境変数 | 説明 |
|---------|------|
| `ANTHROPIC_API_KEY` | APIキー |
| `MIYABI_MODEL` | モデル名 |
| `MIYABI_MAX_TOKENS` | 最大トークン数 |
| `MIYABI_THINKING` | Extended Thinking (true/false) |
---
## プロジェクトルール
### .miyabirulesファイル
プロジェクトルートに `.miyabirules` ファイルを作成することで、プロジェクト固有のルールを定義できます。
### ファイル形式
```yaml
version: 1
rules:
- name: "no-unwrap"
pattern: ".unwrap()"
suggestion: "Use ? operator or proper error handling"
file_extensions: ["rs"]
severity: "warning"
- name: "no-println-debug"
pattern: "println!"
suggestion: "Use tracing macros for logging"
file_extensions: ["rs"]
severity: "info"
agent_preferences:
codegen:
style: "functional"
error_handling: "result"
min_score: 80
review:
min_score: 70
```
### ルールの確認
```bash
./target/release/miyabi rules
```
### 重要度レベル
| レベル | 説明 |
|--------|------|
| `error` | 重大な問題(赤) |
| `warning` | 警告(黄) |
| `info` | 情報(青) |
---
新規利用では `mergegate` を推奨します。`miyabi` は互換 alias です。
## トラブルシューティング
### "API key not found" エラー
### `tasks: 0`
異常ではありません。task が未登録なだけです。
### どの task から始めればよいか分からない
```bash
# 環境変数を確認
echo $ANTHROPIC_API_KEY
# 設定されていない場合
export ANTHROPIC_API_KEY="sk-ant-..."
./target/release/mergegate gate dispatchable
./target/release/mergegate gate dag
./target/release/mergegate gate guide
```
### "Device not configured" エラー
ターミナルがTUIをサポートしていない可能性があります
### 既存 repo に導入済みか分からない
```bash
# 別のターミナルを試すiTerm2, Alacritty等
# または明示的にTERM設定
TERM=xterm-256color ./target/release/miyabi tui
./target/release/mergegate gate status
```
### ビルドエラー
```bash
# Rustを最新版に更新
rustup update
# クリーンビルド
cargo clean
cargo build --release
```
### セッションが保存されない
```bash
# ディレクトリの確認
ls -la ~/.miyabi/sessions/
# なければ作成
mkdir -p ~/.miyabi/sessions
```
### デバッグモード
詳細なログを表示:
```bash
RUST_LOG=debug ./target/release/miyabi tui
```
### ネットワークエラー
```bash
# タイムアウトを延長
# ~/.miyabi/config.toml
[api]
timeout_secs = 300
max_retries = 5
```
---
## サポート
### ヘルプの確認
```bash
# 全体のヘルプ
./target/release/miyabi --help
# サブコマンドのヘルプ
./target/release/miyabi agent --help
./target/release/miyabi sessions --help
```
### 問題報告
GitHub Issues: https://github.com/ShunsukeHayashi/mergegate/issues
### TUIでのヘルプ
TUI起動中に `F1` キーでヘルプを表示できます。
---
## 付録
### 対応モデル
- `claude-sonnet-4-5-20250929` (デフォルト)
- `claude-haiku-4-5-20251001`
- `claude-sonnet-4-20250514`
### ファイル構成
```
~/.miyabi/
├── config.toml # 設定ファイル
└── sessions/ # セッションデータ
├── xxx.json
└── yyy.json
```
### ショートカット早見表
| 操作 | キー |
|------|------|
| 送信 | Enter |
| 終了 | Ctrl+C |
| ヘルプ | F1 |
| コマンドパレット | Ctrl+P |
| 新規セッション | Ctrl+N |
| セッションを開く | Ctrl+O |
| 保存 | Ctrl+S |
---
**Built with Rust, Ratatui, and Claude API**
ledger が無ければ `gate init` に進めます。