npx skills add https://github.com/gaboe/opencode-gitbutler --skill butInstall this skill with the CLI and start using the SKILL.md workflow in your workspace.
Stop managing git branches manually and let your AI agents do the heavy lifting with GitButler.
AI agents generate code at a pace that manual version control can't match. Without automation, you end up with massive commits, messy branch organization, and generic messages that make code review a nightmare.
This plugin bridges the gap by bringing GitButler's virtual branch power directly into your OpenCode agent sessions.
but cursor after-edit.Add to your opencode.json (global or project-level):
{
"plugin": [
"opencode-gitbutler@latest"
]
}
OpenCode will install the plugin automatically on next launch.
brew install gitbutler
See GitButler installation docs for other methods.
The plugin includes a skill that teaches your agent GitButler commands, safety rules, and workflows. Install it as a project-level skill so the agent can load it on demand:
npx skills add https://github.com/gaboe/opencode-gitbutler --skill but --agent opencode --yes
Why? The plugin handles automation (auto-branch, auto-commit), but the agent also needs to know how to use
butcommands directly. Without the skill, the agent falls back to rawgitcommands which break the GitButler workspace.
The plugin automatically:
edit/write through GitButler's after-editThis plugin uses a session-first flow. In practice:
edit/write, the plugin resolves your root session (parent session for subagents).conversation_id from that root session (or from branch_target, when configured).but cursor after-edit with that conversation_id and file path.but cursor stop for that same conversation_id.but rub unassigned changes when attribution is safe,ge-branch-* names,This avoids cross-session branch pollution and keeps subagent edits attached to the parent session branch.
Create .opencode/gitbutler.json in your workspace root to override defaults:
{
// Enable debug logging to .opencode/plugin/debug.log
"log_enabled": true,
// LLM provider and model for commit message generation
"commit_message_provider": "anthropic",
"commit_message_model": "claude-haiku-4-5",
// Timeout for LLM requests (milliseconds)
"llm_timeout_ms": 15000,
// Maximum diff size to send to LLM (characters)
"max_diff_chars": 4000,
// Maximum length of auto-generated branch slugs
"branch_slug_max_length": 50,
// Enable automatic version update checks
"auto_update": true,
// Regex pattern for default branch detection
"default_branch_pattern": "^ge-branch-\\d+$",
// Milliseconds before file lock is considered stale
"stale_lock_ms": 300000,
// Max age of pending notifications before expiry
"notification_max_age_ms": 300000,
// Enable branch inference heuristics in post-stop sweep
"inference_enabled": true,
// Optional: force all sessions onto one branch seed
"branch_target": "",
// Reserved (currently no-op)
"edit_debounce_ms": 200,
"gc_on_session_start": false
}
| Key | Type | Default | Description |
|---|---|---|---|
log_enabled |
boolean | true |
Write debug logs to .opencode/plugin/debug.log |
commit_message_provider |
string | "anthropic" |
LLM provider ID |
commit_message_model |
string | "claude-haiku-4-5" |
Model ID for commit generation |
llm_timeout_ms |
number | 15000 |
Request timeout in milliseconds |
max_diff_chars |
number | 4000 |
Max diff size sent to LLM |
branch_slug_max_length |
number | 50 |
Max auto-generated branch name length |
auto_update |
boolean | true |
Check npm for newer versions |
default_branch_pattern |
string | "^ge-branch-\\d+$" |
Regex for default branch detection |
stale_lock_ms |
number | 300000 |
Lock age threshold before stale cleanup |
notification_max_age_ms |
number | 300000 |
Expiry window for queued state notifications |
inference_enabled |
boolean | true |
Enable branch inference in post-stop sweep |
branch_target |
string | unset | Force all sessions to one branch seed (disables per-session isolation) |
edit_debounce_ms |
number | 200 |
Reserved, currently no-op |
gc_on_session_start |
boolean | false |
Reserved, currently no-op |
All fields are optional. Missing fields use defaults.
How this plugin compares to GitButler's built-in Cursor and Claude Code integrations:
| Feature | Cursor | Claude Code | This Plugin | Status |
|---|---|---|---|---|
| Post-edit hook | after-edit |
PostToolUse | tool.execute.after |
Equal |
| Stop/idle hook | stop |
Stop | session.idle |
Equal |
| Branch creation | get_or_create_session |
get_or_create_session |
via conversation_id |
Equal |
| Auto-assign to existing branch | Internal | Internal | Session-first after-edit + safe post-stop but rub sweep |
Better |
| Branch auto-rename (LLM) | From Cursor DB | From transcript | but reword + user prompt |
Equal |
| Auto-commit on stop | handle_changes() |
handle_changes() |
via but cursor stop |
Equal |
| Commit message (LLM) | OpenAI gpt-4-mini | OpenAI gpt-4-mini | Claude Haiku via OpenCode SDK | Equal |
| Multi-agent session mapping | — | — | resolveSessionRoot() |
Unique |
| File locking (concurrent) | — | 60s wait + retry | 60s poll + stale cleanup | Equal |
| Agent state notifications | — | — | chat.messages.transform |
Unique |
| Hunk-level rub guard | — | — | Skip multi-stack files | Better |
Score: 7 Equal, 4 Better/Unique
For the full architecture breakdown, gap analysis, and known issues, see docs/gitbutler-integration.md.
gitbutler/workspace branch).branch_target is set, all sessions intentionally share one branch seed.edit_debounce_ms and gc_on_session_start are reserved config fields and are currently no-op.docs/gitbutler-integration.md).Error: ⚠ GitButler CLI not found. Install with: brew install gitbutler
Solution: Install GitButler via Homebrew:
brew install gitbutler
The plugin will work without it, but workspace commands will fail at runtime.
If .opencode/gitbutler.json is missing, the plugin uses all defaults. No error is raised.
Enable log_enabled: true in config to write detailed logs to .opencode/plugin/debug.log. Useful for diagnosing branch creation, commit message generation, and state injection issues.
If changes still appear on an unexpected branch:
branch_target is not set in .opencode/gitbutler.json..opencode/plugin/session-map.json and confirm subagent sessions resolve to the expected parent..opencode/plugin/debug.log for after-edit, session-stop, and branch-collision events.but status --json -f and verify where the file is currently assigned.If commit message generation times out, increase llm_timeout_ms in config:
{
"llm_timeout_ms": 30000
}
If diffs are truncated, increase max_diff_chars:
{
"max_diff_chars": 8000
}
See SKILL.md bundled with this package for detailed GitButler workspace commands, multi-agent safety rules, and known issues.
MIT