loop: add configurable branch prefix option
Add -branch-prefix flag to customize git branch prefix instead of
hardcoded 'sketch/' for better integration with different workflows.
Problem Analysis:
The sketch system hardcoded 'sketch/' as the branch prefix throughout
the codebase, making it difficult for users who wanted different
branch naming conventions or needed to integrate with existing
development workflows that used different prefixes.
Implementation Changes:
1. CLI Flag Addition:
- Added -branch-prefix flag to main.go with default 'sketch/'
- Integrated flag into CLIFlags struct and argument parsing
- Maintains backward compatibility with existing workflows
2. Configuration Threading:
- Added BranchPrefix field to AgentConfig struct
- Added BranchPrefix field to ContainerConfig struct
- Modified container argument passing to include branch prefix
- Set sensible default of 'sketch/' when not specified
3. Agent Implementation:
- Added BranchPrefix() method to CodingAgent interface
- Updated precommit tool to use configurable prefix
- Modified git commit handling to use configurable prefix
- Updated all hardcoded 'sketch/' references in logic
4. UI Integration:
- Enhanced termui template to use configurable branch prefix
- Added branch_prefix field to server State response
- Updated webui TypeScript types via auto-generation
- Modified sketch-tool-card to display configurable prefix
5. Container Integration:
- Modified dockerimg to pass branch prefix to inner sketch
- Updated container command arguments construction
- Ensured proper flag threading through container boundary
Technical Details:
- Maintains full backward compatibility with existing usage
- Default behavior unchanged for existing users
- Type-safe implementation with proper error handling
- Clean interface design without type casts or fallbacks
Benefits:
- Enables custom branch naming conventions
- Better integration with team workflows
- Maintains existing sketch functionality
- Improves flexibility for different use cases
Testing:
- Verified default behavior remains unchanged
- Confirmed custom prefix works in both termui and webui
- All existing tests continue to pass
- Build verification successful across all components
This enhancement provides workflow flexibility while maintaining
the reliability and functionality of the existing branch management
system.
Co-Authored-By: sketch <hello@sketch.dev>
Change-ID: s71341dca2cfeeb24k
diff --git a/loop/agent.go b/loop/agent.go
index f5c6e07..fd01f33 100644
--- a/loop/agent.go
+++ b/loop/agent.go
@@ -66,6 +66,9 @@
// Loop begins the agent loop returns only when ctx is cancelled.
Loop(ctx context.Context)
+ // BranchPrefix returns the configured branch prefix
+ BranchPrefix() string
+
CancelTurn(cause error)
CancelToolUse(toolUseID string, cause error) error
@@ -935,11 +938,18 @@
GitRemoteAddr string
// Commit to checkout from Outtie
Commit string
+ // Prefix for git branches created by sketch
+ BranchPrefix string
}
// NewAgent creates a new Agent.
// It is not usable until Init() is called.
func NewAgent(config AgentConfig) *Agent {
+ // Set default branch prefix if not specified
+ if config.BranchPrefix == "" {
+ config.BranchPrefix = "sketch/"
+ }
+
agent := &Agent{
config: config,
ready: make(chan struct{}),
@@ -1312,13 +1322,13 @@
if params.BranchName != cleanBranchName(params.BranchName) {
return nil, fmt.Errorf("branch_name parameter must be alphanumeric hyphenated slug")
}
- branchName := "sketch/" + params.BranchName
+ branchName := a.config.BranchPrefix + params.BranchName
if branchExists(a.workingDir, branchName) {
return nil, fmt.Errorf("branch %q already exists; please choose a different branch name", branchName)
}
a.SetBranch(branchName)
- response := fmt.Sprintf("switched to branch sketch/%q - DO NOT change branches unless explicitly requested", branchName)
+ response := fmt.Sprintf("switched to branch %q - DO NOT change branches unless explicitly requested", branchName)
styleHint, err := claudetool.CommitMessageStyleHint(ctx, a.repoRoot)
if err != nil {
@@ -1338,6 +1348,11 @@
return a.ready
}
+// BranchPrefix returns the configured branch prefix
+func (a *Agent) BranchPrefix() string {
+ return a.config.BranchPrefix
+}
+
func (a *Agent) UserMessage(ctx context.Context, msg string) {
a.pushToOutbox(ctx, AgentMessage{Type: UserMessageType, Content: msg})
a.inbox <- msg
@@ -1815,7 +1830,7 @@
}
func (a *Agent) handleGitCommits(ctx context.Context) ([]*GitCommit, error) {
- msgs, commits, error := a.gitState.handleGitCommits(ctx, a.SessionID(), a.repoRoot, a.SketchGitBaseRef())
+ msgs, commits, error := a.gitState.handleGitCommits(ctx, a.SessionID(), a.repoRoot, a.SketchGitBaseRef(), a.config.BranchPrefix)
for _, msg := range msgs {
a.pushToOutbox(ctx, msg)
}
@@ -1824,7 +1839,7 @@
// handleGitCommits() highlights new commits to the user. When running
// under docker, new HEADs are pushed to a branch according to the title.
-func (ags *AgentGitState) handleGitCommits(ctx context.Context, sessionID string, repoRoot string, baseRef string) ([]AgentMessage, []*GitCommit, error) {
+func (ags *AgentGitState) handleGitCommits(ctx context.Context, sessionID string, repoRoot string, baseRef string, branchPrefix string) ([]AgentMessage, []*GitCommit, error) {
ags.mu.Lock()
defer ags.mu.Unlock()
@@ -1892,7 +1907,7 @@
commits = append(commits, headCommit)
}
- originalBranch := cmp.Or(ags.branchName, "sketch/"+sessionID)
+ originalBranch := cmp.Or(ags.branchName, branchPrefix+sessionID)
branch := originalBranch
// TODO: I don't love the force push here. We could see if the push is a fast-forward, and,