Files
kandev/apps/backend/internal/utility/template/engine.go
T
2026-04-15 13:22:32 +01:00

133 lines
5.1 KiB
Go

package template
import (
"regexp"
"strings"
)
// Context holds the data available for template resolution.
type Context struct {
// Git-related
GitDiff string // Output of git diff --staged or git diff
CommitLog string // Output of git log for the branch
ChangedFiles string // List of changed files
DiffSummary string // Summary of changes (insertions, deletions)
BranchName string // Current branch name
BaseBranch string // Base branch (main/master)
// Task-related
TaskTitle string // Title of the current task
TaskDescription string // Description of the current task
// Session-related
SessionID string // Current session ID
WorkspacePath string // Path to the workspace/worktree
// User input
UserPrompt string // User's original prompt text (for enhance-prompt)
// Conversation
ConversationHistory string // Formatted conversation transcript (for summarize-session)
// Custom key-value pairs for extensibility
Custom map[string]string
}
// Engine resolves template variables in prompts.
type Engine struct{}
// NewEngine creates a new template engine.
func NewEngine() *Engine {
return &Engine{}
}
// templatePattern matches {{Key}} placeholders.
var templatePattern = regexp.MustCompile(`\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}`)
// Resolve resolves template variables in the given prompt using the provided context.
// Uses {{Key}} format (no dot) for consistency with scriptengine.
// Unknown placeholders are left unchanged.
func (e *Engine) Resolve(promptTemplate string, ctx *Context) (string, error) {
return e.ResolveWithOptions(promptTemplate, ctx, ResolveOptions{})
}
// ResolveOptions configures template resolution.
type ResolveOptions struct {
// MissingAsEmpty substitutes unknown placeholders with an empty string
// instead of leaving them as {{Var}}. Used for sessionless utility execution
// where some context variables (e.g. GitDiff, TaskTitle) are meaningless.
MissingAsEmpty bool
}
// ResolveWithOptions resolves template variables with the given options.
func (e *Engine) ResolveWithOptions(promptTemplate string, ctx *Context, opts ResolveOptions) (string, error) {
if ctx == nil {
ctx = &Context{}
}
data := e.buildTemplateData(ctx)
result := templatePattern.ReplaceAllStringFunc(promptTemplate, func(match string) string {
key := strings.TrimSuffix(strings.TrimPrefix(match, "{{"), "}}")
if val, ok := data[key]; ok {
return val
}
if opts.MissingAsEmpty {
return ""
}
return match
})
return result, nil
}
// buildTemplateData creates the data map for template execution.
func (e *Engine) buildTemplateData(ctx *Context) map[string]string {
data := map[string]string{
"GitDiff": ctx.GitDiff,
"CommitLog": ctx.CommitLog,
"ChangedFiles": ctx.ChangedFiles,
"DiffSummary": ctx.DiffSummary,
"BranchName": ctx.BranchName,
"BaseBranch": ctx.BaseBranch,
"TaskTitle": ctx.TaskTitle,
"TaskDescription": ctx.TaskDescription,
"SessionID": ctx.SessionID,
"WorkspacePath": ctx.WorkspacePath,
"UserPrompt": ctx.UserPrompt,
"ConversationHistory": ctx.ConversationHistory,
}
// Add custom key-value pairs
for k, v := range ctx.Custom {
data[k] = v
}
return data
}
// AvailableVariables returns the list of available template variables.
func (e *Engine) AvailableVariables() []VariableInfo {
return []VariableInfo{
{Name: "GitDiff", Description: "Output of git diff --staged or git diff", Example: "diff --git a/main.go...", Category: "git"},
{Name: "CommitLog", Description: "Git log for the current branch", Example: "abc123 Fix bug in handler", Category: "git"},
{Name: "ChangedFiles", Description: "List of changed files", Example: "src/main.go\nsrc/utils.go", Category: "git"},
{Name: "DiffSummary", Description: "Summary of changes (files, insertions, deletions)", Example: "3 files, +50 -10", Category: "git"},
{Name: "BranchName", Description: "Current git branch name", Example: "feature/add-login", Category: "git"},
{Name: "BaseBranch", Description: "Base branch (main/master/develop)", Example: "main", Category: "git"},
{Name: "TaskTitle", Description: "Title of the current task", Example: "Add user authentication", Category: "task"},
{Name: "TaskDescription", Description: "Description of the current task", Example: "Implement OAuth2 login flow", Category: "task"},
{Name: "SessionID", Description: "Current session ID", Example: "sess_abc123", Category: "session"},
{Name: "WorkspacePath", Description: "Path to the workspace/worktree", Example: "/home/user/project", Category: "session"},
{Name: "UserPrompt", Description: "User's original prompt text", Example: "Fix the login bug", Category: "input"},
{Name: "ConversationHistory", Description: "Formatted conversation transcript", Example: "User: Fix the bug\nAgent: I'll look into...", Category: "session"},
}
}
// VariableInfo describes a template variable.
type VariableInfo struct {
Name string `json:"name"`
Description string `json:"description"`
Example string `json:"example"`
Category string `json:"category"` // "git", "task", "session"
}