agent

package
v0.1.51 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2026 License: Apache-2.0 Imports: 37 Imported by: 0

README

Agent Package

The agent orchestrates an AI-powered setup flow for Tusk Drift. It uses Claude to guide users through SDK installation, configuration, and testing via an interactive TUI.

Architecture

flowchart TB
    subgraph Agent["agent.go"]
        A[Agent.Run] --> PM[PhaseManager]
        A --> CC[ClaudeClient]
        A --> TUI[TUIModel]
    end

    subgraph Phases["phases.go"]
        PM --> P1[Detect Language]
        PM --> P2[Check Compatibility]
        PM --> P3[Gather Info]
        PM --> P4[...]
        PM --> PN[Summary]
    end

    subgraph Tools["executor.go + tools/"]
        CC -->|tool_use| TE[Tool Executors]
        TE --> FS[filesystem.go]
        TE --> PR[process.go]
        TE --> HT[http.go]
        TE --> US[user.go]
        TE --> TK[tusk.go]
    end

    subgraph Prompts["prompts/"]
        P1 -.->|instructions| SYS[system.md]
        P1 -.->|instructions| PH1[phase_*.md]
    end

    CC <-->|API| Claude[(Claude API)]
    TUI -->|renders| Terminal

Key Components

agent/
├── agent.go      # Main loop: iterates phases, sends messages to Claude, handles tool calls
├── phases.go     # Defines phases, their tools, and state transitions
├── executor.go   # Tool registry, schema definitions, and executor dispatch
├── types.go      # Message types, API types, State, Config
├── api.go        # Claude API client
├── tui.go        # Terminal UI (bubbletea) for progress, logs, and user interaction
├── prompts.go    # Embeds prompt markdown files
├── prompts/      # System prompt and per-phase instructions (*.md)
└── tools/        # Tool implementations (filesystem, process, http, etc.)

Data Flow

  1. Phase Loop (agent.go): Runs each phase until transition_phase is called
  2. Prompt Assembly: System prompt + phase instructions + current state → Claude
  3. Tool Execution: Claude returns tool_use → executor runs it → result returned
  4. State Updates: transition_phase updates State and advances to next phase

Adding a New Phase

1. Create the prompt file

Create prompts/phase_<name>.md with instructions for the phase:

## Phase: My New Phase

Your goal is to accomplish X.

### Instructions
1. Do A
2. Do B

When complete, call `transition_phase` with:
{
  "results": {
    "my_field": "value"
  }
}
2. Embed the prompt

In prompts.go, add:

//go:embed prompts/phase_my_new.md
var PhaseMyNewPrompt string
3. Define the phase

In phases.go, add the phase function:

func myNewPhase() *Phase {
    return &Phase{
        ID:           "my_new",
        Name:         "My New Phase",       // Displayed in TUI
        Description:  "Brief description",
        Instructions: PhaseMyNewPrompt,
        Tools: Tools(
            ToolReadFile,
            ToolWriteFile,
            // ... tools this phase needs
            ToolTransitionPhase,
        ),
        Required:      true,  // false = can be skipped
        MaxIterations: 20,    // 0 = use default (50)
        // Optional: inject dynamic content when entering this phase
        OnEnter: func(state *State) string {
            return "Additional context based on state"
        },
    }
}
4. Register in defaultPhases()

Add your phase to the ordered list:

func defaultPhases() []*Phase {
    return []*Phase{
        detectLanguagePhase(),
        checkCompatibilityPhase(),
        myNewPhase(),  // ← insert in correct order
        // ...
    }
}
5. Update State (if needed)

If your phase produces results, add fields to State in types.go:

type State struct {
    // ...
    MyField string `json:"my_field"`
}

Then handle them in UpdateState() in phases.go:

if v, ok := results["my_field"].(string); ok {
    pm.state.MyField = v
}

Adding a New Tool

1. Implement the executor

Create or edit a file in tools/ (e.g., tools/mytool.go):

package tools

import "encoding/json"

func MyTool(input json.RawMessage) (string, error) {
    var params struct {
        Param1 string `json:"param1"`
    }
    if err := json.Unmarshal(input, &params); err != nil {
        return "", err
    }

    // Do work...

    return "Result message", nil
}
2. Register the tool

In executor.go:

a) Add the constant:

const (
    // ...
    ToolMyTool ToolName = "my_tool"
)

b) Add the definition in toolDefinitions():

ToolMyTool: {
    Name:        ToolMyTool,
    Description: "What this tool does and when to use it",
    InputSchema: json.RawMessage(`{
        "type": "object",
        "properties": {
            "param1": {
                "type": "string",
                "description": "Description of param1"
            }
        },
        "required": ["param1"]
    }`),
    RequiresConfirmation: false,  // true = ask user before executing
},

c) Wire up the executor in RegisterTools():

executorMap := map[ToolName]ToolExecutor{
    // ...
    ToolMyTool: tools.MyTool,
}
3. Add to phases

In phases.go, include the tool in relevant phases:

Tools: Tools(
    ToolReadFile,
    ToolMyTool,  // ← add here
    ToolTransitionPhase,
),

Notes

  • Model: Agent is currently configured to use Claude Sonnet 4.5. We may allow model customization in the future, if necessary.
  • Sandboxing: Commands run via run_command are sandboxed using fence. See tools/process.go.

Documentation

Index

Constants

View Source
const (
	PhaseTimeout         = 15 * time.Minute // Max time per phase
	APITimeout           = 5 * time.Minute  // Max time for a single API call
	ToolTimeout          = 3 * time.Minute  // Max time for a single tool execution
	DefaultMaxIterations = 50               // Default max iterations if phase doesn't specify
	MaxTotalTokens       = 500000           // Max total tokens before warning
	MaxAPIRetries        = 3                // Max retries for API errors
)

Timeouts

View Source
const (
	ErrMaxIterations = "exceeded maximum iterations without completing phase"
	ErrMaxTokens     = "exceeded maximum token usage"
)

Error messages

Variables

View Source
var PhaseCheckCompatibilityNodejsPrompt string

Node.js specific prompts

View Source
var PhaseCheckCompatibilityPythonPrompt string

Python specific prompts

View Source
var PhaseCloudAuthPrompt string

Cloud setup prompts

View Source
var PhaseCloudConfigureRecordingPrompt string
View Source
var PhaseCloudCreateApiKeyPrompt string
View Source
var PhaseCloudCreateServicePrompt string
View Source
var PhaseCloudDetectRepoPrompt string
View Source
var PhaseCloudSummaryPrompt string
View Source
var PhaseCloudUploadTracesPrompt string
View Source
var PhaseCloudValidateSuitePrompt string
View Source
var PhaseCloudVerifyAccessPrompt string
View Source
var PhaseComplexTestPrompt string
View Source
var PhaseConfirmAppStartsPrompt string

Shared prompts (language-agnostic)

View Source
var PhaseCreateConfigPrompt string
View Source
var PhaseDetectLanguagePrompt string
View Source
var PhaseEligibilityCheckPrompt string

Eligibility check prompt

View Source
var PhaseGatherInfoNodejsPrompt string
View Source
var PhaseGatherInfoPythonPrompt string
View Source
var PhaseInstrumentSDKNodejsPrompt string
View Source
var PhaseInstrumentSDKPythonPrompt string
View Source
var PhaseSimpleTestPrompt string
View Source
var PhaseSummaryPrompt string
View Source
var PhaseVerifyComplexTestPrompt string
View Source
var PhaseVerifyRestorePrompt string
View Source
var PhaseVerifySetupPrompt string

Verify mode prompts

View Source
var PhaseVerifySimpleTestPrompt string
View Source
var SystemPrompt string

Functions

func GetCheckCompatibilityPrompt added in v0.1.34

func GetCheckCompatibilityPrompt(projectType string) string

GetCheckCompatibilityPrompt returns the appropriate compatibility check prompt for the project type.

func GetGatherInfoPrompt added in v0.1.34

func GetGatherInfoPrompt(projectType string) string

GetGatherInfoPrompt returns the appropriate gather info prompt for the project type.

func GetInstrumentSDKPrompt added in v0.1.34

func GetInstrumentSDKPrompt(projectType string) string

GetInstrumentSDKPrompt returns the appropriate SDK instrumentation prompt for the project type.

func PhasesSummary

func PhasesSummary() string

PhasesSummary returns a summary of all phases

func PrintIntroHeadless added in v0.1.39

func PrintIntroHeadless(isProxyMode, skipToCloud, verifyMode bool)

PrintIntroHeadless prints a simple intro for headless mode (no confirmation needed for scripts)

func RecoveryGuidance

func RecoveryGuidance() string

RecoveryGuidance returns a message explaining how to resume after a failure

func RunIntroScreen added in v0.1.39

func RunIntroScreen(isProxyMode, skipToCloud, verifyMode bool) (bool, error)

RunIntroScreen runs the intro screen and returns true if user wants to continue

func ValidateEligibilityReport added in v0.1.38

func ValidateEligibilityReport(report *EligibilityReport) error

ValidateEligibilityReport validates the structure and content of an eligibility report Returns an error if validation fails, nil if valid

func WriteEligibilityReport added in v0.1.38

func WriteEligibilityReport(workDir string, report *EligibilityReport) error

WriteEligibilityReport writes the report to the .tusk directory

Types

type APIError

type APIError struct {
	Type    string `json:"type"`
	Message string `json:"message"`
}

APIError represents an error from the Claude API

type APIMode added in v0.1.43

type APIMode string

APIMode represents how the client connects to the LLM

const (
	// APIModeDirect connects directly to Anthropic API (BYOK)
	APIModeDirect APIMode = "direct"
	// APIModeProxy connects through Tusk backend proxy
	APIModeProxy APIMode = "proxy"
)

type APIResponse

type APIResponse struct {
	ID           string    `json:"id"`
	Type         string    `json:"type"`
	Role         string    `json:"role"`
	Content      []Content `json:"content"`
	Model        string    `json:"model"`
	StopReason   string    `json:"stop_reason"` // "end_turn", "tool_use", "max_tokens"
	StopSequence *string   `json:"stop_sequence"`
	Usage        Usage     `json:"usage"`
}

APIResponse from Claude API

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent orchestrates the AI-powered setup process

func New

func New(cfg Config) (*Agent, error)

New creates a new Agent, with Claude client based on API mode

func (*Agent) Run

func (a *Agent) Run(parentCtx context.Context) error

Run executes the agent with TUI or in headless mode

func (*Agent) SetTracker

func (a *Agent) SetTracker(tracker *analytics.Tracker)

SetTracker sets the analytics tracker for the agent

type AgentLogger added in v0.1.35

type AgentLogger struct {
	// contains filtered or unexported fields
}

AgentLogger handles logging of agent activity to a file

func NewAgentLogger added in v0.1.35

func NewAgentLogger(workDir string, mode string) (*AgentLogger, error)

NewAgentLogger creates a new logger that writes to .tusk/logs/setup-<datetime>.log mode should be "TUI" or "Headless"

func (*AgentLogger) Close added in v0.1.35

func (l *AgentLogger) Close(status CloseStatus, err error) error

Close closes the log file with the given status

func (*AgentLogger) FilePath added in v0.1.35

func (l *AgentLogger) FilePath() string

FilePath returns the path to the log file

func (*AgentLogger) LogError added in v0.1.35

func (l *AgentLogger) LogError(err error)

LogError logs an error

func (*AgentLogger) LogMessage added in v0.1.35

func (l *AgentLogger) LogMessage(message string)

LogMessage logs an agent message

func (*AgentLogger) LogPhaseStart added in v0.1.35

func (l *AgentLogger) LogPhaseStart(phaseName, phaseDesc string, phaseNum, totalPhases int)

LogPhaseStart logs the start of a phase

func (*AgentLogger) LogThinking added in v0.1.35

func (l *AgentLogger) LogThinking(thinking bool)

LogThinking logs when the agent is thinking

func (*AgentLogger) LogToolComplete added in v0.1.35

func (l *AgentLogger) LogToolComplete(toolName string, success bool, output string)

LogToolComplete logs the completion of a tool call

func (*AgentLogger) LogToolStart added in v0.1.35

func (l *AgentLogger) LogToolStart(toolName, input string)

LogToolStart logs the start of a tool call

func (*AgentLogger) LogUserInput added in v0.1.35

func (l *AgentLogger) LogUserInput(question, response string)

LogUserInput logs a user input request and response

func (*AgentLogger) LogUserSelect added in v0.1.35

func (l *AgentLogger) LogUserSelect(question, selectedID, selectedLabel string)

LogUserSelect logs a user selection request and response

type AgentUI added in v0.1.35

type AgentUI interface {
	// Lifecycle
	Start() error // Initialize the UI (e.g., start TUI program)
	Stop()        // Clean up the UI

	// ShowIntro displays the intro screen with animation and description.
	// Returns true if user wants to continue, false if they quit.
	// isProxyMode determines whether to show the proxy mode note.
	// skipToCloud determines whether to show the cloud-only phases description.
	ShowIntro(isProxyMode, skipToCloud, verifyMode bool) (bool, error)

	// Phase updates
	PhaseChange(name, desc string, phaseNum, totalPhases int)
	UpdatePhaseList(phaseNames []string)

	// Agent output
	AgentText(text string, streaming bool)
	Thinking(thinking bool)

	// Tool execution feedback
	ToolStart(name, input string)
	ToolComplete(name string, success bool, output string)

	// Status updates
	SidebarUpdate(key, value string)
	Error(err error)
	FatalError(err error)
	Completed(workDir string)
	EligibilityCompleted(workDir string)
	Aborted(reason string)

	// Interactive prompts - all blocking, return results directly
	// Returns (response, cancelled)
	PromptUserInput(question string) (string, bool)
	// Returns (selectedID, selectedLabel, cancelled)
	PromptUserSelect(question string, options []SelectOption) (string, string, bool)
	// PromptPermission asks for permission to execute a tool.
	// commandPrefixes is non-empty for run_command/start_background_process to enable command-level granularity.
	// Returns "approve", "approve_tool_type", "approve_commands:<csv>", "approve_all", "deny", or "deny:<alternative>"
	PromptPermission(toolName, preview string, commandPrefixes []string) string
	// Returns true if user wants to kill the process
	PromptKillPort(port int) bool
	// Returns (rerun, cancelled)
	PromptRerun() (bool, bool)
	// Returns (continueWithCloud, cancelled)
	PromptCloudSetup() (bool, bool)

	// GetFinalOutput returns any final output to display after UI stops (TUI only)
	GetFinalOutput() string
}

AgentUI abstracts the user interface for the agent, allowing both TUI and headless modes to share the same core agent logic. All methods are blocking and return results directly.

func NewAgentUI added in v0.1.35

func NewAgentUI(ctx context.Context, cancel context.CancelFunc, headless bool, phaseNames []string, hideProgressBar bool) AgentUI

NewAgentUI creates the appropriate UI implementation based on the mode

type ClaudeClient

type ClaudeClient struct {
	// contains filtered or unexported fields
}

ClaudeClient handles communication with the Claude API

func NewClaudeClient

func NewClaudeClient(apiKey, model string) (*ClaudeClient, error)

NewClaudeClient creates a new Claude API client (legacy constructor for BYOK)

func NewClaudeClientWithConfig added in v0.1.43

func NewClaudeClientWithConfig(cfg ClaudeClientConfig) (*ClaudeClient, error)

NewClaudeClientWithConfig creates a new Claude API client with the given configuration

func (*ClaudeClient) CreateMessage

func (c *ClaudeClient) CreateMessage(
	ctx context.Context,
	system string,
	messages []Message,
	tools []Tool,
) (*APIResponse, error)

CreateMessage sends a message to Claude and returns the response (non-streaming)

func (*ClaudeClient) CreateMessageStreaming

func (c *ClaudeClient) CreateMessageStreaming(
	ctx context.Context,
	system string,
	messages []Message,
	tools []Tool,
	callback StreamCallback,
) (*APIResponse, error)

CreateMessageStreaming sends a message to Claude and streams the response

func (*ClaudeClient) SetSessionID added in v0.1.43

func (c *ClaudeClient) SetSessionID(sessionID string)

SetSessionID sets the session ID for request correlation

type ClaudeClientConfig added in v0.1.43

type ClaudeClientConfig struct {
	Mode        APIMode
	APIKey      string // For direct mode
	BearerToken string // For proxy mode
	Model       string
	BaseURL     string // Custom base URL (for proxy mode)
}

ClaudeClientConfig holds configuration for creating a ClaudeClient

type CloseStatus added in v0.1.35

type CloseStatus int

CloseStatus represents the final status when closing the logger

const (
	StatusCompleted CloseStatus = iota
	StatusCancelled
	StatusFailed
)

type Config

type Config struct {
	// API configuration
	APIMode     APIMode // Direct (BYOK) or Proxy
	APIKey      string  // For direct mode
	BearerToken string  // For proxy mode
	ProxyURL    string  // For proxy mode

	Model           string
	SystemPrompt    string
	MaxTokens       int
	WorkDir         string
	SkipPermissions bool   // Skip permission prompts for consequential actions
	DisableProgress bool   // Don't save or resume from PROGRESS.md
	SkipToCloud     bool   // Skip local setup and go directly to cloud setup (for testing)
	PrintMode       bool   // Headless mode - no TUI, stream to stdout
	OutputLogs      bool   // Output all logs to a file in .tusk/logs/
	EligibilityOnly bool   // Only run eligibility check, output JSON and exit
	VerifyMode      bool   // Verify existing setup works by re-recording and replaying
	UserGuidance    string // Additional user-provided guidance for the agent
}

Config holds agent configuration

type Content

type Content struct {
	Type      string          `json:"type"` // "text", "tool_use", "tool_result"
	Text      string          `json:"text,omitempty"`
	ID        string          `json:"id,omitempty"`          // For tool_use
	Name      string          `json:"name,omitempty"`        // For tool_use
	Input     json.RawMessage `json:"input,omitempty"`       // For tool_use
	ToolUseID string          `json:"tool_use_id,omitempty"` // For tool_result
	Content   string          `json:"content,omitempty"`     // For tool_result (as string)
	IsError   bool            `json:"is_error,omitempty"`    // For tool_result
}

Content represents a content block in a message

type EligibilityReport added in v0.1.38

type EligibilityReport struct {
	Services map[string]ServiceEligibility `json:"services"`
	Summary  EligibilitySummary            `json:"summary"`
}

EligibilityReport is the complete output structure

func ParseEligibilityReport added in v0.1.38

func ParseEligibilityReport(jsonStr string) (*EligibilityReport, error)

ParseEligibilityReport parses a JSON string into an EligibilityReport Returns an error if parsing or validation fails

type EligibilityStatus added in v0.1.38

type EligibilityStatus string

EligibilityStatus represents the compatibility status of a service

const (
	StatusCompatible          EligibilityStatus = "compatible"
	StatusPartiallyCompatible EligibilityStatus = "partially_compatible"
	StatusNotCompatible       EligibilityStatus = "not_compatible"
)

type EligibilitySummary added in v0.1.38

type EligibilitySummary struct {
	TotalServices       int `json:"total_services"`
	Compatible          int `json:"compatible"`
	PartiallyCompatible int `json:"partially_compatible"`
	NotCompatible       int `json:"not_compatible"`
}

EligibilitySummary contains aggregate statistics

type HeadlessUI added in v0.1.35

type HeadlessUI struct {
	// contains filtered or unexported fields
}

HeadlessUI implements AgentUI for terminal output without TUI

func NewHeadlessUI added in v0.1.35

func NewHeadlessUI() *HeadlessUI

NewHeadlessUI creates a new headless UI

func (*HeadlessUI) Aborted added in v0.1.35

func (u *HeadlessUI) Aborted(reason string)

Aborted displays abort message

func (*HeadlessUI) AgentText added in v0.1.35

func (u *HeadlessUI) AgentText(text string, streaming bool)

AgentText displays agent output text

func (*HeadlessUI) Completed added in v0.1.35

func (u *HeadlessUI) Completed(workDir string)

Completed displays completion message

func (*HeadlessUI) EligibilityCompleted added in v0.1.38

func (u *HeadlessUI) EligibilityCompleted(workDir string)

EligibilityCompleted displays eligibility check completion message

func (*HeadlessUI) Error added in v0.1.35

func (u *HeadlessUI) Error(err error)

Error displays a non-fatal error

func (*HeadlessUI) FatalError added in v0.1.35

func (u *HeadlessUI) FatalError(err error)

FatalError displays a fatal error

func (*HeadlessUI) GetFinalOutput added in v0.1.35

func (u *HeadlessUI) GetFinalOutput() string

GetFinalOutput returns empty string for headless mode

func (*HeadlessUI) PhaseChange added in v0.1.35

func (u *HeadlessUI) PhaseChange(name, desc string, phaseNum, totalPhases int)

PhaseChange displays a phase change header

func (*HeadlessUI) PromptCloudSetup added in v0.1.35

func (u *HeadlessUI) PromptCloudSetup() (bool, bool)

PromptCloudSetup asks the user if they want to continue with cloud setup

func (*HeadlessUI) PromptKillPort added in v0.1.35

func (u *HeadlessUI) PromptKillPort(port int) bool

PromptKillPort asks the user if they want to kill a process on a port

func (*HeadlessUI) PromptPermission added in v0.1.35

func (u *HeadlessUI) PromptPermission(toolName, preview string, commandPrefixes []string) string

PromptPermission asks the user for permission to execute a tool. In headless mode, this auto-approves since headless implies skip-permissions by default. If we get here (e.g., with --no-skip-permissions), just auto-approve to avoid blocking.

func (*HeadlessUI) PromptRerun added in v0.1.35

func (u *HeadlessUI) PromptRerun() (bool, bool)

PromptRerun asks the user if they want to rerun setup

func (*HeadlessUI) PromptUserInput added in v0.1.35

func (u *HeadlessUI) PromptUserInput(question string) (string, bool)

PromptUserInput prompts the user for text input

func (*HeadlessUI) PromptUserSelect added in v0.1.35

func (u *HeadlessUI) PromptUserSelect(question string, options []SelectOption) (string, string, bool)

PromptUserSelect prompts the user to select from options

func (*HeadlessUI) ShowIntro added in v0.1.39

func (u *HeadlessUI) ShowIntro(isProxyMode, skipToCloud, verifyMode bool) (bool, error)

ShowIntro displays the intro screen for headless mode (no confirmation for scripts)

func (*HeadlessUI) SidebarUpdate added in v0.1.35

func (u *HeadlessUI) SidebarUpdate(key, value string)

SidebarUpdate is a no-op for headless mode (no sidebar)

func (*HeadlessUI) Start added in v0.1.35

func (u *HeadlessUI) Start() error

Start displays the headless mode header

func (*HeadlessUI) Stop added in v0.1.35

func (u *HeadlessUI) Stop()

Stop is a no-op for headless mode

func (*HeadlessUI) Thinking added in v0.1.35

func (u *HeadlessUI) Thinking(thinking bool)

Thinking shows/hides the thinking indicator

func (*HeadlessUI) ToolComplete added in v0.1.35

func (u *HeadlessUI) ToolComplete(name string, success bool, output string)

ToolComplete displays tool completion status

func (*HeadlessUI) ToolStart added in v0.1.35

func (u *HeadlessUI) ToolStart(name, input string)

ToolStart displays tool start notification

func (*HeadlessUI) UpdatePhaseList added in v0.1.35

func (u *HeadlessUI) UpdatePhaseList(phaseNames []string)

UpdatePhaseList is a no-op for headless mode (no visual phase list)

type IntroModel added in v0.1.39

type IntroModel struct {
	// contains filtered or unexported fields
}

IntroModel is the bubbletea model for the intro screen

func NewIntroModel added in v0.1.39

func NewIntroModel(isProxyMode, skipToCloud, verifyMode bool) *IntroModel

NewIntroModel creates a new intro screen model

func (*IntroModel) Init added in v0.1.39

func (m *IntroModel) Init() tea.Cmd

func (*IntroModel) ShouldContinue added in v0.1.39

func (m *IntroModel) ShouldContinue() bool

ShouldContinue returns true if the user pressed a key to continue (not quit)

func (*IntroModel) Update added in v0.1.39

func (m *IntroModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (*IntroModel) View added in v0.1.39

func (m *IntroModel) View() string

type LogEntry added in v0.1.35

type LogEntry struct {
	Timestamp time.Time `json:"timestamp"`
	Type      string    `json:"type"` // "phase", "tool_start", "tool_complete", "message", "error", "thinking"
	Phase     string    `json:"phase,omitempty"`
	Tool      string    `json:"tool,omitempty"`
	Input     string    `json:"input,omitempty"`
	Output    string    `json:"output,omitempty"`
	Success   bool      `json:"success,omitempty"`
	Message   string    `json:"message,omitempty"`
	Error     string    `json:"error,omitempty"`
}

LogEntry represents a single log entry

type Message

type Message struct {
	Role    string    `json:"role"` // "user", "assistant"
	Content []Content `json:"content"`
}

Message represents a conversation message

type PackageInfo added in v0.1.38

type PackageInfo struct {
	Packages  []string `json:"packages"`
	Reasoning string   `json:"reasoning"`
}

PackageInfo contains information about packages in a category

type Phase

type Phase struct {
	ID            string
	Name          string
	Description   string
	Instructions  string
	Tools         []PhaseTool // Which tools are available in this phase
	Required      bool        // Must complete, or can skip?
	MaxIterations int         // Max iterations for this phase (0 = use default)
	// OnEnter is called when entering this phase, returns additional context to append to instructions
	OnEnter func(state *State) string
}

Phase represents a distinct phase in the agent's workflow

type PhaseError

type PhaseError struct {
	Phase   string `json:"phase"`
	Message string `json:"message"`
	Fatal   bool   `json:"fatal"`
}

PhaseError represents an error that occurred during a phase

type PhaseManager

type PhaseManager struct {
	// contains filtered or unexported fields
}

PhaseManager manages the agent's progress through phases

func NewPhaseManager

func NewPhaseManager() *PhaseManager

NewPhaseManager creates a new PhaseManager with default phases

func (*PhaseManager) AddCloudPhases added in v0.1.35

func (pm *PhaseManager) AddCloudPhases()

AddCloudPhases adds the cloud setup phases after local setup is complete

func (*PhaseManager) AdvancePhase

func (pm *PhaseManager) AdvancePhase() (*Phase, error)

AdvancePhase moves to the next phase

func (*PhaseManager) CurrentPhase

func (pm *PhaseManager) CurrentPhase() *Phase

CurrentPhase returns the current phase

func (*PhaseManager) GetPhaseNames

func (pm *PhaseManager) GetPhaseNames() []string

GetPhaseNames returns all phase names in order

func (*PhaseManager) GetState

func (pm *PhaseManager) GetState() *State

GetState returns the current state

func (*PhaseManager) HasCloudPhases added in v0.1.35

func (pm *PhaseManager) HasCloudPhases() bool

HasCloudPhases returns true if cloud phases have been added

func (*PhaseManager) HasTransitioned

func (pm *PhaseManager) HasTransitioned() bool

HasTransitioned returns true if a transition occurred this iteration

func (*PhaseManager) IsComplete

func (pm *PhaseManager) IsComplete() bool

IsComplete returns true if all phases are done

func (*PhaseManager) PhaseTransitionTool

func (pm *PhaseManager) PhaseTransitionTool() ToolExecutor

PhaseTransitionTool creates the transition_phase tool executor

func (*PhaseManager) ResetTransitionFlag

func (pm *PhaseManager) ResetTransitionFlag()

ResetTransitionFlag resets the transition flag

func (*PhaseManager) RestoreDiscoveredInfo

func (pm *PhaseManager) RestoreDiscoveredInfo(info map[string]string)

RestoreDiscoveredInfo restores discovered information from parsed progress file

func (*PhaseManager) RestoreSetupProgress

func (pm *PhaseManager) RestoreSetupProgress(progress map[string]bool)

RestoreSetupProgress restores setup progress flags from parsed progress file

func (*PhaseManager) SetCloudOnlyMode added in v0.1.35

func (pm *PhaseManager) SetCloudOnlyMode()

SetCloudOnlyMode replaces local phases with cloud-only phases (for --skip-to-cloud testing)

func (*PhaseManager) SetEligibilityOnlyMode added in v0.1.38

func (pm *PhaseManager) SetEligibilityOnlyMode()

SetEligibilityOnlyMode replaces all phases with the eligibility check phase

func (*PhaseManager) SetPreviousProgress

func (pm *PhaseManager) SetPreviousProgress(progress string)

SetPreviousProgress sets the progress from a previous interrupted run

func (*PhaseManager) SetVerifyMode added in v0.1.49

func (pm *PhaseManager) SetVerifyMode()

SetVerifyMode replaces all phases with verify-only phases

func (*PhaseManager) SkipToPhase

func (pm *PhaseManager) SkipToPhase(phaseName string) bool

SkipToPhase skips to a specific phase by name, returning true if found

func (*PhaseManager) StateAsContext

func (pm *PhaseManager) StateAsContext() string

StateAsContext returns the current state as a string for the prompt

func (*PhaseManager) UpdateState

func (pm *PhaseManager) UpdateState(results map[string]interface{})

UpdateState updates the state with results from a phase

type PhaseTool

type PhaseTool struct {
	Name                 ToolName
	RequiresConfirmation bool          // Require user confirmation before executing
	CustomTimeout        time.Duration // Override default timeout (0 = use default)
}

PhaseTool represents a tool available in a phase with optional configuration

func GetPhaseToolConfig

func GetPhaseToolConfig(phase *Phase, name ToolName) *PhaseTool

GetPhaseToolConfig returns the PhaseTool config for a given tool in a phase (for checking confirmation, timeout, etc.)

func T

func T(name ToolName) PhaseTool

T creates a PhaseTool with default configuration (short name for use with builder pattern)

func Tools

func Tools(names ...ToolName) []PhaseTool

Tools is a helper to create []PhaseTool from ToolName constants (simple case, no config)

func (PhaseTool) WithConfirmation

func (pt PhaseTool) WithConfirmation() PhaseTool

WithConfirmation returns a copy of the PhaseTool that requires user confirmation

func (PhaseTool) WithTimeout

func (pt PhaseTool) WithTimeout(d time.Duration) PhaseTool

WithTimeout returns a copy of the PhaseTool with a custom timeout

type ProcessManager

type ProcessManager = tools.ProcessManager

ProcessManager wraps the tools.ProcessManager for external access

func NewProcessManager

func NewProcessManager(workDir string) *ProcessManager

NewProcessManager creates a new ProcessManager

type Runtime added in v0.1.38

type Runtime string

Runtime represents a detected runtime environment

const (
	RuntimeNodeJS Runtime = "nodejs"
	RuntimePython Runtime = "python"
	RuntimeOther  Runtime = "other"
)

type SelectOption added in v0.1.35

type SelectOption struct {
	ID    string `json:"id"`
	Label string `json:"label"`
}

SelectOption represents a selectable option for ask_user_select

type ServiceEligibility added in v0.1.38

type ServiceEligibility struct {
	Status              EligibilityStatus `json:"status"`
	StatusReasoning     string            `json:"status_reasoning"`
	Runtime             Runtime           `json:"runtime"`
	Framework           string            `json:"framework,omitempty"`
	SupportedPackages   *PackageInfo      `json:"supported_packages,omitempty"`
	UnsupportedPackages *PackageInfo      `json:"unsupported_packages,omitempty"`
	UnknownPackages     *PackageInfo      `json:"unknown_packages,omitempty"`
}

ServiceEligibility contains eligibility information for a single service

type State

type State struct {
	// Discovery results
	ProjectType      string `json:"project_type"`       // "nodejs", "python", "go", etc.
	PackageManager   string `json:"package_manager"`    // "npm", "yarn", "pnpm" (Node.js); "pip", "poetry", "uv", "pipenv" (Python)
	ModuleSystem     string `json:"module_system"`      // "esm", "cjs" (Node.js specific)
	Framework        string `json:"framework"`          // "fastapi", "flask", "django" (Python specific); "express", "fastify", "hono" (Node.js)
	EntryPoint       string `json:"entry_point"`        // e.g., "src/server.ts", "main.py", "app/main.py"
	StartCommand     string `json:"start_command"`      // e.g., "npm run start", "uvicorn app.main:app"
	Port             string `json:"port"`               // e.g., "3000", "8000"
	HealthEndpoint   string `json:"health_endpoint"`    // e.g., "/health"
	DockerType       string `json:"docker_type"`        // "none", "dockerfile", "compose"
	ServiceName      string `json:"service_name"`       // e.g., "my-service"
	HasExternalCalls bool   `json:"has_external_calls"` // Does it make outbound HTTP/DB calls?

	// Compatibility check results
	CompatibilityWarnings []string `json:"compatibility_warnings"` // e.g., ["mongodb@6.3.0 not instrumented"]

	// Progress tracking
	AppStartsWithoutSDK bool `json:"app_starts_without_sdk"`
	SDKInstalled        bool `json:"sdk_installed"`
	SDKInstrumented     bool `json:"sdk_instrumented"`
	ConfigCreated       bool `json:"config_created"`
	SimpleTestPassed    bool `json:"simple_test_passed"`
	ComplexTestPassed   bool `json:"complex_test_passed"`

	// Error tracking
	Errors   []PhaseError `json:"errors"`
	Warnings []string     `json:"warnings"`

	// Eligibility mode
	EligibilityReport string `json:"eligibility_report,omitempty"` // Raw JSON report
	UserGuidance      string `json:"-"`                            // Input guidance, not persisted

	// Cloud setup state
	IsAuthenticated       bool    `json:"is_authenticated"`
	UserEmail             string  `json:"user_email"`
	UserId                string  `json:"user_id"`
	BearerToken           string  `json:"bearer_token,omitempty"`
	SelectedClientID      string  `json:"selected_client_id"`
	SelectedClientName    string  `json:"selected_client_name"`
	GitRepoOwner          string  `json:"git_repo_owner"`
	GitRepoName           string  `json:"git_repo_name"`
	CodeHostingType       string  `json:"code_hosting_type"` // "github" or "gitlab"
	CloudServiceID        string  `json:"cloud_service_id"`
	ApiKeyCreated         bool    `json:"api_key_created"`
	ApiKeyValue           string  `json:"api_key_value,omitempty"` // Only set during session, not persisted
	SamplingRate          float64 `json:"sampling_rate"`
	ExportSpans           bool    `json:"export_spans"`
	EnableEnvVarRecording bool    `json:"enable_env_var_recording"`

	// Trace upload state
	TracesUploaded       int  `json:"traces_uploaded"`
	TraceUploadSuccess   bool `json:"trace_upload_success"`
	TraceUploadAttempted bool `json:"trace_upload_attempted"`

	// Suite validation state
	SuiteValidationSuccess   bool `json:"suite_validation_success"`
	TestsInSuite             int  `json:"tests_in_suite"`
	SuiteValidationAttempted bool `json:"suite_validation_attempted"`

	// Verify mode state
	OriginalSamplingRate          float64 `json:"original_sampling_rate,omitempty"`
	OriginalExportSpans           bool    `json:"original_export_spans"`
	OriginalEnableEnvVarRecording bool    `json:"original_enable_env_var_recording"`
	VerifySimplePassed            bool    `json:"verify_simple_passed,omitempty"`
	VerifyComplexPassed           bool    `json:"verify_complex_passed,omitempty"`
}

State tracks what the agent has learned and done

type StreamCallback

type StreamCallback func(event StreamEvent)

StreamCallback is called with streaming updates

type StreamEvent

type StreamEvent struct {
	Type       string // "text", "tool_use_start", "tool_use_input", "done"
	Text       string
	ToolName   string
	ToolID     string
	ToolInput  string
	StopReason string
}

StreamEvent represents a streaming event

type TUIModel

type TUIModel struct {
	// contains filtered or unexported fields
}

TUIModel is the bubbletea model for the agent TUI

func NewTUIModel

func NewTUIModel(ctx context.Context, cancel context.CancelFunc, phaseNames []string, hideProgressBar bool) *TUIModel

NewTUIModel creates a new TUI model

func (*TUIModel) GetFinalOutput

func (m *TUIModel) GetFinalOutput() string

GetFinalOutput returns the log content for printing after exit Renders a complete view similar to the TUI layout

func (*TUIModel) Init

func (m *TUIModel) Init() tea.Cmd

func (*TUIModel) RequestPermission

func (m *TUIModel) RequestPermission(program *tea.Program, toolName, preview string, commandPrefixes []string) string

RequestPermission asks the user for permission to execute a tool. commandPrefixes is non-empty for run_command/start_background_process to enable command-level granularity. Returns "approve", "approve_tool_type", "approve_commands:<csv>", "approve_all", "deny", or "deny:<alternative>".

func (*TUIModel) RequestPortConflict

func (m *TUIModel) RequestPortConflict(program *tea.Program, port int) bool

func (*TUIModel) RequestUserInput

func (m *TUIModel) RequestUserInput(program *tea.Program, question string) string

func (*TUIModel) RequestUserSelect added in v0.1.35

func (m *TUIModel) RequestUserSelect(program *tea.Program, question string, options []SelectOption) string

RequestUserSelect asks the user to select from a list of options. Returns the ID of the selected option, or empty string if cancelled.

func (*TUIModel) SendAborted

func (m *TUIModel) SendAborted(program *tea.Program, reason string)

func (*TUIModel) SendAgentText

func (m *TUIModel) SendAgentText(program *tea.Program, text string, streaming bool)

func (*TUIModel) SendCloudSetupPrompt added in v0.1.35

func (m *TUIModel) SendCloudSetupPrompt(program *tea.Program, responseCh chan bool)

SendCloudSetupPrompt prompts the user whether to continue with cloud setup

func (*TUIModel) SendCompleted

func (m *TUIModel) SendCompleted(program *tea.Program, workDir string)

func (*TUIModel) SendEligibilityCompleted added in v0.1.38

func (m *TUIModel) SendEligibilityCompleted(program *tea.Program, workDir string)

func (*TUIModel) SendError

func (m *TUIModel) SendError(program *tea.Program, err error)

func (*TUIModel) SendFatalError

func (m *TUIModel) SendFatalError(program *tea.Program, err error)

func (*TUIModel) SendPhaseChange

func (m *TUIModel) SendPhaseChange(program *tea.Program, name, desc string, num, total int)

func (*TUIModel) SendRerunConfirm

func (m *TUIModel) SendRerunConfirm(program *tea.Program, responseCh chan bool)

func (*TUIModel) SendSidebarUpdate

func (m *TUIModel) SendSidebarUpdate(program *tea.Program, key, value string)

func (*TUIModel) SendThinking

func (m *TUIModel) SendThinking(program *tea.Program, thinking bool)

func (*TUIModel) SendToolComplete

func (m *TUIModel) SendToolComplete(program *tea.Program, name string, success bool, output string)

func (*TUIModel) SendToolStart

func (m *TUIModel) SendToolStart(program *tea.Program, name, input string)

func (*TUIModel) Update

func (m *TUIModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (*TUIModel) UpdateTodoItems added in v0.1.35

func (m *TUIModel) UpdateTodoItems(program *tea.Program, phaseNames []string)

UpdateTodoItems updates the todo list with new phase names (used when adding cloud phases)

func (*TUIModel) View

func (m *TUIModel) View() string

type TUIUI added in v0.1.35

type TUIUI struct {
	// contains filtered or unexported fields
}

TUIUI implements AgentUI using the bubbletea TUI framework

func NewTUIUI added in v0.1.35

func NewTUIUI(ctx context.Context, cancel context.CancelFunc, phaseNames []string, hideProgressBar bool) *TUIUI

NewTUIUI creates a new TUI-based UI

func (*TUIUI) Aborted added in v0.1.35

func (u *TUIUI) Aborted(reason string)

Aborted notifies the UI that setup was aborted

func (*TUIUI) AgentText added in v0.1.35

func (u *TUIUI) AgentText(text string, streaming bool)

AgentText displays agent output text

func (*TUIUI) Completed added in v0.1.35

func (u *TUIUI) Completed(workDir string)

Completed notifies the UI that setup is complete

func (*TUIUI) EligibilityCompleted added in v0.1.38

func (u *TUIUI) EligibilityCompleted(workDir string)

EligibilityCompleted notifies the UI that eligibility check is complete

func (*TUIUI) Error added in v0.1.35

func (u *TUIUI) Error(err error)

Error displays a non-fatal error

func (*TUIUI) FatalError added in v0.1.35

func (u *TUIUI) FatalError(err error)

FatalError displays a fatal error and prepares for exit

func (*TUIUI) GetFinalOutput added in v0.1.35

func (u *TUIUI) GetFinalOutput() string

GetFinalOutput returns the final output to display after the TUI exits

func (*TUIUI) PhaseChange added in v0.1.35

func (u *TUIUI) PhaseChange(name, desc string, phaseNum, totalPhases int)

PhaseChange notifies the UI of a phase change

func (*TUIUI) PromptCloudSetup added in v0.1.35

func (u *TUIUI) PromptCloudSetup() (bool, bool)

PromptCloudSetup asks the user if they want to continue with cloud setup

func (*TUIUI) PromptKillPort added in v0.1.35

func (u *TUIUI) PromptKillPort(port int) bool

PromptKillPort asks the user if they want to kill a process on a port

func (*TUIUI) PromptPermission added in v0.1.35

func (u *TUIUI) PromptPermission(toolName, preview string, commandPrefixes []string) string

PromptPermission asks the user for permission to execute a tool

func (*TUIUI) PromptRerun added in v0.1.35

func (u *TUIUI) PromptRerun() (bool, bool)

PromptRerun asks the user if they want to rerun setup

func (*TUIUI) PromptUserInput added in v0.1.35

func (u *TUIUI) PromptUserInput(question string) (string, bool)

PromptUserInput prompts the user for text input

func (*TUIUI) PromptUserSelect added in v0.1.35

func (u *TUIUI) PromptUserSelect(question string, options []SelectOption) (string, string, bool)

PromptUserSelect prompts the user to select from options

func (*TUIUI) Run added in v0.1.35

func (u *TUIUI) Run() (tea.Model, error)

Run runs the TUI program and blocks until it exits Returns the final model and any error

func (*TUIUI) ShowIntro added in v0.1.39

func (u *TUIUI) ShowIntro(isProxyMode, skipToCloud, verifyMode bool) (bool, error)

ShowIntro displays the intro screen with animation

func (*TUIUI) SidebarUpdate added in v0.1.35

func (u *TUIUI) SidebarUpdate(key, value string)

SidebarUpdate updates a sidebar info item

func (*TUIUI) Start added in v0.1.35

func (u *TUIUI) Start() error

Start initializes and starts the TUI program

func (*TUIUI) Stop added in v0.1.35

func (u *TUIUI) Stop()

Stop is a no-op for TUI - cleanup happens via the program

func (*TUIUI) Thinking added in v0.1.35

func (u *TUIUI) Thinking(thinking bool)

Thinking shows/hides the thinking indicator

func (*TUIUI) ToolComplete added in v0.1.35

func (u *TUIUI) ToolComplete(name string, success bool, output string)

ToolComplete notifies the UI that a tool has completed

func (*TUIUI) ToolStart added in v0.1.35

func (u *TUIUI) ToolStart(name, input string)

ToolStart notifies the UI that a tool is starting

func (*TUIUI) UpdatePhaseList added in v0.1.35

func (u *TUIUI) UpdatePhaseList(phaseNames []string)

UpdatePhaseList updates the list of phases (used when cloud phases are added)

type Tool

type Tool struct {
	Name        string          `json:"name"`
	Description string          `json:"description"`
	InputSchema json.RawMessage `json:"input_schema"`
}

Tool defines a tool the agent can use

func FilterToolsForPhase

func FilterToolsForPhase(allTools []Tool, phase *Phase) []Tool

FilterToolsForPhase filters tool definitions to only those available in the current phase

func RegisterTools

func RegisterTools(workDir string, pm *ProcessManager, phaseMgr *PhaseManager) ([]Tool, map[string]ToolExecutor)

RegisterTools initializes the tool registry with executors and returns API-compatible formats

type ToolDefinition

type ToolDefinition struct {
	Name                 ToolName
	Description          string
	InputSchema          json.RawMessage
	Executor             ToolExecutor // Set at runtime via RegisterTools
	RequiresConfirmation bool         // Whether this tool requires user confirmation by default
}

ToolDefinition is the single source of truth for a tool's metadata and implementation

type ToolExecutor

type ToolExecutor func(input json.RawMessage) (string, error)

ToolExecutor executes a tool and returns the result

type ToolName

type ToolName string

ToolName is a type-safe identifier for tools

const (
	ToolReadFile               ToolName = "read_file"
	ToolWriteFile              ToolName = "write_file"
	ToolListDirectory          ToolName = "list_directory"
	ToolGrep                   ToolName = "grep"
	ToolPatchFile              ToolName = "patch_file"
	ToolRunCommand             ToolName = "run_command"
	ToolStartBackgroundProcess ToolName = "start_background_process"
	ToolStopBackgroundProcess  ToolName = "stop_background_process"
	ToolGetProcessLogs         ToolName = "get_process_logs"
	ToolWaitForReady           ToolName = "wait_for_ready"
	ToolHTTPRequest            ToolName = "http_request"
	ToolAskUser                ToolName = "ask_user"
	ToolAskUserSelect          ToolName = "ask_user_select"
	ToolTuskValidateConfig     ToolName = "tusk_validate_config"
	ToolTuskList               ToolName = "tusk_list"
	ToolTuskRun                ToolName = "tusk_run"
	ToolTransitionPhase        ToolName = "transition_phase"
	ToolAbortSetup             ToolName = "abort_setup"
	ToolResetCloudProgress     ToolName = "reset_cloud_progress"

	// Cloud setup tools
	ToolCloudCheckAuth         ToolName = "cloud_check_auth"
	ToolCloudLogin             ToolName = "cloud_login"
	ToolCloudWaitForLogin      ToolName = "cloud_wait_for_login"
	ToolCloudGetClients        ToolName = "cloud_get_clients"
	ToolCloudSelectClient      ToolName = "cloud_select_client"
	ToolCloudDetectGitRepo     ToolName = "cloud_detect_git_repo"
	ToolCloudVerifyRepoAccess  ToolName = "cloud_verify_repo_access"
	ToolCloudGetAuthURL        ToolName = "cloud_get_auth_url"
	ToolCloudOpenBrowser       ToolName = "cloud_open_browser"
	ToolCloudCreateService     ToolName = "cloud_create_service"
	ToolCloudCreateApiKey      ToolName = "cloud_create_api_key"
	ToolCloudCheckApiKeyExists ToolName = "cloud_check_api_key_exists"
	ToolCloudSaveConfig        ToolName = "cloud_save_config"
	ToolCloudUploadTraces      ToolName = "cloud_upload_traces"
	ToolCloudRunValidation     ToolName = "cloud_run_validation"
)

Tool name constants - use these instead of raw strings for type safety

func AllToolNames

func AllToolNames() []ToolName

AllToolNames returns all possible tool names

func GetToolsForPhase

func GetToolsForPhase(phase *Phase) []ToolName

GetToolsForPhase returns the tool names available for a phase

type ToolRegistry

type ToolRegistry struct {
	// contains filtered or unexported fields
}

ToolRegistry holds all tool definitions, keyed by name

func GetRegistry

func GetRegistry() *ToolRegistry

GetRegistry returns the global tool registry (available after RegisterTools is called)

func (*ToolRegistry) All

func (r *ToolRegistry) All() []*ToolDefinition

All returns all tool definitions

func (*ToolRegistry) Get

func (r *ToolRegistry) Get(name ToolName) *ToolDefinition

Get returns a tool definition by name

type Usage

type Usage struct {
	InputTokens  int `json:"input_tokens"`
	OutputTokens int `json:"output_tokens"`
}

Usage tracks token usage

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL