feat: /auto-commit — auto git commit after code changes#22
Conversation
Toggle with /auto-commit. After write tools, auto stages all changes and commits with LLM-generated message. Runs after auto-test if both enabled.
📝 WalkthroughWalkthroughThis PR introduces an auto-commit feature to the application. It adds a new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant TUI as TUI/Commands
participant CLI as CLI Handler
participant Runtime
participant Git
User->>TUI: Enter /auto-commit
TUI->>TUI: Set PendingCommand::AutoCommitToggle
CLI->>Runtime: Toggle auto_commit flag
Runtime->>CLI: Return ON/OFF status
CLI->>User: Display status message
User->>Runtime: Execute write tool (e.g., write_file)
Runtime->>Runtime: Check auto_commit enabled
Runtime->>Git: git add -A
Runtime->>Git: git diff --cached --stat
Runtime->>Runtime: Generate commit message
Runtime->>Git: git commit -m <message>
Git-->>Runtime: Commit result
Runtime->>User: Display commit output/error
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@mc/crates/mc-core/src/runtime.rs`:
- Around line 495-515: The auto-commit path currently ignores test results and
can commit even if the test command failed to launch or exited non-zero; add and
check a tests_passed flag (e.g. a bool variable scoped with the other
auto_test_cmd logic) so that before the auto-commit block (inside the if
self.auto_commit { ... }) you skip committing unless tests_passed is true;
ensure the test-running code (where auto_test_cmd is executed) sets tests_passed
= false on spawn/launch errors and when the child process exits with non-zero
status, and only sets tests_passed = true on a successful zero exit, then update
the commit condition (with tool_calls and had_writes) to also require
tests_passed before running git add/commit and calling
generate_commit_message/on_event (ProviderEvent::ToolOutputDelta).
- Around line 499-510: Replace the blocking std::process::Command calls in the
auto-commit block with tokio::process::Command and await their outputs (similar
to the earlier auto-test block); for each command used (git add, git diff
--cached --stat, git commit -m) use tokio::process::Command, await the Output,
check output.status.success(), and read both stdout and stderr to decide which
ProviderEvent::ToolOutputDelta to emit; update references around
generate_commit_message, on_event, and ProviderEvent::ToolOutputDelta so that
failures (non-success status or spawn errors) emit a clear error message
including stderr, while success emits the trimmed stdout as before.
In `@mc/crates/mc-tui/src/commands.rs`:
- Around line 111-113: The new handler registers
PendingCommand::AutoCommitToggle but you must also add the command to the
discoverability structures: add the "/auto-commit" entry (with its brief
description) into the App::SLASH_COMMANDS collection and update the slash-help
rendering (the function/constant that builds the `/help` text for slash
commands) so the command appears in help output and Tab completion; ensure the
string and description match the handler name PendingCommand::AutoCommitToggle
so completion and help stay in sync.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 93b92b0b-5089-4d08-aacf-cf5cd2313b72
📒 Files selected for processing (4)
mc/crates/mc-cli/src/main.rsmc/crates/mc-core/src/runtime.rsmc/crates/mc-tui/src/app.rsmc/crates/mc-tui/src/commands.rs
| // Auto-commit: stage and commit with LLM-generated message | ||
| if self.auto_commit { | ||
| let had_writes = tool_calls.iter().any(|t| matches!(t.as_str(), "write_file" | "edit_file" | "batch_edit" | "apply_patch")); | ||
| if had_writes { | ||
| let _ = std::process::Command::new("git").args(["add", "-A"]).output(); | ||
| if let Ok(diff) = std::process::Command::new("git").args(["diff", "--cached", "--stat"]).output() { | ||
| let stat = String::from_utf8_lossy(&diff.stdout); | ||
| if !stat.trim().is_empty() { | ||
| on_event(&ProviderEvent::ToolOutputDelta("📦 Auto-committing...\n".into())); | ||
| let msg = self.generate_commit_message(provider, &stat).await; | ||
| match std::process::Command::new("git").args(["commit", "-m", &msg]).output() { | ||
| Ok(o) => { | ||
| let out = String::from_utf8_lossy(&o.stdout); | ||
| on_event(&ProviderEvent::ToolOutputDelta(format!("✓ {}\n", out.trim()))); | ||
| } | ||
| Err(e) => on_event(&ProviderEvent::ToolOutputDelta(format!("commit error: {e}\n"))), | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Require an explicit test success before auto-commit.
When auto_test_cmd is enabled, a failure to launch the test command in the preceding block falls through to this commit path, so the repo can still be committed without any passing test result. Track a tests_passed flag and skip auto-commit on both execution errors and non-zero exits to match the PR contract.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mc/crates/mc-core/src/runtime.rs` around lines 495 - 515, The auto-commit
path currently ignores test results and can commit even if the test command
failed to launch or exited non-zero; add and check a tests_passed flag (e.g. a
bool variable scoped with the other auto_test_cmd logic) so that before the
auto-commit block (inside the if self.auto_commit { ... }) you skip committing
unless tests_passed is true; ensure the test-running code (where auto_test_cmd
is executed) sets tests_passed = false on spawn/launch errors and when the child
process exits with non-zero status, and only sets tests_passed = true on a
successful zero exit, then update the commit condition (with tool_calls and
had_writes) to also require tests_passed before running git add/commit and
calling generate_commit_message/on_event (ProviderEvent::ToolOutputDelta).
| let _ = std::process::Command::new("git").args(["add", "-A"]).output(); | ||
| if let Ok(diff) = std::process::Command::new("git").args(["diff", "--cached", "--stat"]).output() { |
There was a problem hiding this comment.
Don't stage the whole repository here.
git add -A will commit unrelated local edits and any test artifacts produced just before this block, not only files written by this turn. Because tool_calls only records tool names, even a failed write attempt plus pre-existing changes can become an unexpected commit. Stage only the paths that were successfully modified in this turn.
| let _ = std::process::Command::new("git").args(["add", "-A"]).output(); | ||
| if let Ok(diff) = std::process::Command::new("git").args(["diff", "--cached", "--stat"]).output() { | ||
| let stat = String::from_utf8_lossy(&diff.stdout); | ||
| if !stat.trim().is_empty() { | ||
| on_event(&ProviderEvent::ToolOutputDelta("📦 Auto-committing...\n".into())); | ||
| let msg = self.generate_commit_message(provider, &stat).await; | ||
| match std::process::Command::new("git").args(["commit", "-m", &msg]).output() { | ||
| Ok(o) => { | ||
| let out = String::from_utf8_lossy(&o.stdout); | ||
| on_event(&ProviderEvent::ToolOutputDelta(format!("✓ {}\n", out.trim()))); | ||
| } | ||
| Err(e) => on_event(&ProviderEvent::ToolOutputDelta(format!("commit error: {e}\n"))), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cd mc && find . -name "runtime.rs" -type f | head -5Repository: kienbui1995/mc-code
Length of output: 94
🏁 Script executed:
cd mc && git ls-files | grep -E "runtime\.rs|src/" | head -20Repository: kienbui1995/mc-code
Length of output: 668
🏁 Script executed:
cd mc && ls -la crates/mc-core/src/ | head -20Repository: kienbui1995/mc-code
Length of output: 1227
🏁 Script executed:
cd mc && head -20 crates/mc-core/src/runtime.rs && wc -l crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 720
🏁 Script executed:
cd mc && sed -n '490,520p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 1746
🏁 Script executed:
cd mc && sed -n '450,530p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 4419
🏁 Script executed:
cd mc && sed -n '300,350p' crates/mc-core/src/runtime.rs | grep -A 20 "fn run_turn"Repository: kienbui1995/mc-code
Length of output: 45
🏁 Script executed:
cd mc && sed -n '200,300p' crates/mc-core/src/runtime.rs | grep -B 5 "async fn run_turn"Repository: kienbui1995/mc-code
Length of output: 222
🏁 Script executed:
cd mc && grep -n "async fn run_turn" crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 93
🏁 Script executed:
cd mc && sed -n '247,270p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 1191
🏁 Script executed:
cd mc && sed -n '450,475p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 1380
🏁 Script executed:
cd mc && grep -n "tokio::process::Command" crates/mc-core/src/runtime.rs | head -10Repository: kienbui1995/mc-code
Length of output: 413
🏁 Script executed:
cd mc && sed -n '495,515p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 1480
🏁 Script executed:
cd mc && sed -n '470,495p' crates/mc-core/src/runtime.rs | tail -20Repository: kienbui1995/mc-code
Length of output: 1254
🏁 Script executed:
cd mc && sed -n '540,600p' crates/mc-core/src/runtime.rs | grep -A 30 "generate_commit_message"Repository: kienbui1995/mc-code
Length of output: 45
🏁 Script executed:
cd mc && grep -n "fn generate_commit_message" crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 166
🏁 Script executed:
cd mc && sed -n '199,220p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 934
🏁 Script executed:
cd mc && sed -n '471,490p' crates/mc-core/src/runtime.rsRepository: kienbui1995/mc-code
Length of output: 1341
Replace std::process::Command with tokio::process::Command and check exit status.
These synchronous git calls block the Tokio executor inside an async function. Additionally, Ok(output) only confirms the process started—git commit can still fail (hooks, missing config, conflicts) but the code prints ✓ anyway while ignoring stderr. Notice the auto-test block above (lines 471–490) correctly uses tokio::process::Command with .await and checks output.status.success().
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mc/crates/mc-core/src/runtime.rs` around lines 499 - 510, Replace the
blocking std::process::Command calls in the auto-commit block with
tokio::process::Command and await their outputs (similar to the earlier
auto-test block); for each command used (git add, git diff --cached --stat, git
commit -m) use tokio::process::Command, await the Output, check
output.status.success(), and read both stdout and stderr to decide which
ProviderEvent::ToolOutputDelta to emit; update references around
generate_commit_message, on_event, and ProviderEvent::ToolOutputDelta so that
failures (non-success status or spawn errors) emit a clear error message
including stderr, while success emits the trimmed stdout as before.
| "/auto-commit" => { | ||
| app.pending_command = Some(PendingCommand::AutoCommitToggle); | ||
| } |
There was a problem hiding this comment.
Complete the discoverability wiring for /auto-commit.
This adds the handler, but the command is still missing from the /help text and App::SLASH_COMMANDS, so help output and Tab completion stay stale.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mc/crates/mc-tui/src/commands.rs` around lines 111 - 113, The new handler
registers PendingCommand::AutoCommitToggle but you must also add the command to
the discoverability structures: add the "/auto-commit" entry (with its brief
description) into the App::SLASH_COMMANDS collection and update the slash-help
rendering (the function/constant that builds the `/help` text for slash
commands) so the command appears in help output and Tab completion; ensure the
string and description match the handler name PendingCommand::AutoCommitToggle
so completion and help stay in sync.
Toggle with
/auto-commit. After write tools execute, auto-stages and commits with LLM-generated message.Works with
/auto-test: test first, commit only if tests pass.162 tests pass.
Summary by CodeRabbit
Release Notes
/auto-commitcommand to toggle automatic Git commits.