exponential

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AgentRegistry = []AgentConfig{
	{Name: "Generic Agent", File: "AGENTS.md", Format: "markdown"},
	{Name: "Gemini", File: "GEMINI.md", Format: "markdown"},
	{Name: "Cursor", File: ".cursorrules", Format: "plain"},
	{Name: "Windsurf", File: ".windsurfrules", Format: "plain"},
	{Name: "GitHub Copilot", File: ".github/copilot-instructions.md", Format: "markdown", NeedsDir: true},
	{Name: "Cline", File: ".clinerules", Format: "markdown"},
	{Name: "Roo Code", File: ".roorules", Format: "markdown"},
	{Name: "Aider", File: "CONVENTIONS.md", Format: "markdown"},
	{Name: "Continue", File: ".continue/rule./xpo.md", Format: "markdown", NeedsDir: true},
}

AgentRegistry contains all supported AI agent configurations

View Source
var ErrLocalOnly = errors.New("operation requires local mode")

ErrLocalOnly is returned when a local-only operation (git branch, merge, archive) is attempted on a remote transport.

Functions

func AppendAgentInstructions

func AppendAgentInstructions(agent AgentConfig, prefix string) error

AppendAgentInstructions appends xpo instructions to an agent file

func BranchExists

func BranchExists(name string) bool

BranchExists checks whether a local branch with the given name exists.

func CheckGitHooks

func CheckGitHooks() []string

CheckGitHooks returns a list of active git hooks found.

func CheckGitRepo

func CheckGitRepo() bool

CheckGitRepo checks if the current directory is a git repository.

func CheckGithubWorkflows

func CheckGithubWorkflows() bool

CheckGithubWorkflows checks for existence of GitHub workflow files.

func CheckoutBranch

func CheckoutBranch(name string) error

CheckoutBranch switches to an existing branch.

func CreateAndCheckoutBranch

func CreateAndCheckoutBranch(name, base string) error

CreateAndCheckoutBranch creates a new branch from base and checks it out.

func CurrentBranch

func CurrentBranch() string

CurrentBranch returns the name of the currently checked-out branch.

func DefaultBranch

func DefaultBranch() string

DefaultBranch returns the name of the default branch by inspecting the remote HEAD ref. Falls back to "main" if detection fails.

func EnsureMCPConfig

func EnsureMCPConfig() error

EnsureMCPConfig creates or updates .mcp.json to include a xpo MCP server entry.

func FilterIssues

func FilterIssues(issues []*model.Issue, opts FilterOptions, currentUser string) []*model.Issue

FilterIssues applies filtering logic to a list of issues.

func FormatInboxItem

func FormatInboxItem(item InboxItem, me string) string

FormatInboxItem renders a single inbox item as a one-line human sentence from the perspective of `me` (used to render "to you").

func GenerateAgentDocs

func GenerateAgentDocs(prefix string) string

GenerateAgentDocs generates the xpo agent documentation content

func GenerateUpdateTemplate

func GenerateUpdateTemplate(i *model.Issue) string

GenerateUpdateTemplate generates text content for editing an issue.

func GetCommitDiffText

func GetCommitDiffText(sha string) string

GetCommitDiffText returns the unified diff for a single commit.

func GetCompletionInstallCmd

func GetCompletionInstallCmd(shell string) string

GetCompletionInstallCmd returns the command to install completion for the given shell

func GetDiffText

func GetDiffText(branch, base string) string

GetDiffText returns the unified diff as a string.

func GitCommit

func GitCommit(msg string)

GitCommit stages and commits the issues.db file.

func IsMeaningfulActivityEvent

func IsMeaningfulActivityEvent(evt model.Event) bool

func IsWorkingTreeClean

func IsWorkingTreeClean() bool

func ParseTimeFilter

func ParseTimeFilter(input string) (time.Time, error)

ParseTimeFilter parses a time string (duration or date) into a time.Time.

func ParseUpdateContent

func ParseUpdateContent(content string, original *model.Issue) (*model.UpdatePayload, error)

ParseUpdateContent parses edited text content into an UpdatePayload.

func ProjectIssues

func ProjectIssues(events []model.Event) map[string]*model.Issue

func ProjectIssuesWithConfig

func ProjectIssuesWithConfig(events []model.Event, cfg *config.Config) map[string]*model.Issue

ProjectIssuesWithConfig projects issues and applies config-driven automations.

func RemoteBranchExists

func RemoteBranchExists(name string) bool

RemoteBranchExists checks whether a remote branch with the given name exists.

func RunMigrations

func RunMigrations(currentVersion int) (int, error)

RunMigrations runs all pending migrations up to the current DataModelVersion.

func Slugify

func Slugify(s string) string

Slugify converts a string into a URL/branch-friendly slug. Lowercase, non-alphanumeric runs replaced with hyphens, truncated to ~50 characters on a word boundary.

func SortIssues

func SortIssues(issues map[string]*model.Issue) []*model.Issue

func UpdateConfigVersion

func UpdateConfigVersion(newVersion int) error

UpdateConfigVersion updates the version field in config.yaml

func ValidateUser

func ValidateUser(user string) error

ValidateUser validates that a user string matches the expected format "Name <email>".

func WithGitLock

func WithGitLock(fn func() error) error

WithGitLock acquires an exclusive file lock on .xpo/git.lock before running fn, preventing concurrent git-mutating operations from corrupting the working tree. The lock is released when fn returns or if the process crashes.

Types

type AgentConfig

type AgentConfig struct {
	Name     string // Agent name (e.g., "Cursor", "GitHub Copilot")
	File     string // Primary file path (e.g., ".cursorrules")
	Format   string // "markdown" or "plain"
	NeedsDir bool   // If true, parent directory must be created
}

AgentConfig defines an AI agent and its instruction file location

type AgentDetectionResult

type AgentDetectionResult struct {
	Agent                AgentConfig
	Exists               bool
	HasExponentialConfig bool
}

AgentDetectionResult contains information about a detected agent file

func DetectAgentFiles

func DetectAgentFiles() []AgentDetectionResult

DetectAgentFiles scans for existing agent instruction files

type AgentExecutor

type AgentExecutor interface {
	Run(ctx context.Context, prompt string) (*AgentResult, error)
	Name() string
	Stats() AgentStats
}

AgentExecutor runs prompts against an AI agent.

func NewAgentExecutor

func NewAgentExecutor(agent string) AgentExecutor

NewAgentExecutor creates the appropriate executor for the given agent name.

type AgentResult

type AgentResult struct {
	Output    string
	Duration  time.Duration
	TokensIn  int
	TokensOut int
	CostUSD   float64
}

AgentResult holds the output and usage from a single agent call.

type AgentStats

type AgentStats struct {
	Calls     int
	Duration  time.Duration
	TokensIn  int
	TokensOut int
	CostUSD   float64
}

AgentStats holds accumulated usage across multiple calls.

type ArchiveStats

type ArchiveStats struct {
	ActiveEvents   []model.Event
	ArchivedEvents []model.Event
	DoneCount      int
	DeletedCount   int
	KeptCount      int
	TotalArchive   int
}

type Client

type Client struct {
	Transport    Transport
	Config       *config.Config
	Collapse     bool
	UserOverride string
	OnBehalfOf   string
	Source       string
	// contains filtered or unexported fields
}

Client manages the interaction with the xpo issue tracker. It delegates data operations to a Transport (local or remote) and keeps git-only operations (start, merge, review) as direct methods.

func NewClient

func NewClient(cfg *config.Config) *Client

NewClient creates a new Client with the given configuration. By default it uses a LocalTransport. When cfg.Remote.URL is set, a RemoteTransport is used instead.

func (*Client) AddArtifact

func (c *Client) AddArtifact(issueID, artifactType, filename, content string) error

func (*Client) AddComment

func (c *Client) AddComment(issueID, text string) error

func (*Client) AddIssue

func (c *Client) AddIssue(payload model.CreatePayload) (*model.Issue, error)

func (*Client) CheckDuplicates

func (c *Client) CheckDuplicates(title string) ([]*model.Issue, error)

func (*Client) DeleteArtifact

func (c *Client) DeleteArtifact(issueID, filename string) error

func (*Client) DeleteIssue

func (c *Client) DeleteIssue(id string, reason string, cascade ...bool) error

func (*Client) DeleteSpec

func (c *Client) DeleteSpec(issueID string) error

func (*Client) DeleteWalkthrough

func (c *Client) DeleteWalkthrough(issueID string) error

func (*Client) DriveIssue

func (c *Client) DriveIssue(opts DriveOptions) (*DriveResult, error)

func (*Client) FillLocalBranchStats

func (c *Client) FillLocalBranchStats(issue *model.Issue)

FillLocalBranchStats computes BranchStats from a local branch when no remote branch was detected. Checks both the current branch and any local branch whose name contains the issue ID.

func (*Client) FindIssue

func (c *Client) FindIssue(id string) (*model.Issue, []*model.Issue, bool, error)

func (*Client) GetArchiveStats

func (c *Client) GetArchiveStats(days int, keep int) (*ArchiveStats, error)

GetArchiveStats calculates which issues would be archived.

func (*Client) GetInbox

func (c *Client) GetInbox(since time.Time) ([]InboxItem, error)

func (*Client) GetIssue

func (c *Client) GetIssue(id string) (*model.Issue, error)

func (*Client) GetUser

func (c *Client) GetUser() string

func (*Client) IssueIDFromBranch

func (c *Client) IssueIDFromBranch(branch string) (*model.Issue, error)

IssueIDFromBranch attempts to find an issue whose ID matches a segment of the given branch name.

func (*Client) ListArtifacts

func (c *Client) ListArtifacts(issueID string) ([]model.ArtifactSummary, error)

func (*Client) ListIssues

func (c *Client) ListIssues(opts FilterOptions) ([]*model.Issue, error)

func (*Client) MergeIssue

func (c *Client) MergeIssue(id string, opts MergeOptions) (*MergeResult, error)

MergeIssue merges the issue's branch into the default branch, records a MERGE event, and transitions the issue to DONE.

func (*Client) PerformArchive

func (c *Client) PerformArchive(stats *ArchiveStats) error

PerformArchive executes the archiving operation.

func (*Client) ReadArtifact

func (c *Client) ReadArtifact(issueID, filename string) (string, error)

func (*Client) ReadSpec

func (c *Client) ReadSpec(issueID string) (string, error)

func (*Client) ReadWalkthrough

func (c *Client) ReadWalkthrough(issueID string) (string, error)

func (*Client) ResolveReviewIssue

func (c *Client) ResolveReviewIssue(idOrEmpty string) (*model.Issue, error)

ResolveReviewIssue resolves an issue for review — either by explicit ID or by inferring from the current branch. If the issue has no BranchStats from remote detection, it fills them from the local branch.

func (*Client) StartWork

func (c *Client) StartWork(id string, force bool) (branchName string, msgs []string, err error)

StartWork transitions an issue to DOING and, when in a git repo, creates (or checks out) a branch named <issue-id>/<slugified-title>.

func (*Client) UpdateIssue

func (c *Client) UpdateIssue(id string, payload model.UpdatePayload, action string) ([]string, error)

func (*Client) WriteSpec

func (c *Client) WriteSpec(issueID, content string) error

func (*Client) WriteWalkthrough

func (c *Client) WriteWalkthrough(issueID, content string) error

type CommitDetail

type CommitDetail struct {
	SHA     string       `json:"sha"`
	Subject string       `json:"subject"`
	Body    string       `json:"body,omitempty"`
	Author  string       `json:"author"`
	Date    string       `json:"date"`
	Files   []CommitFile `json:"files"`
}

func GetCommitDetail

func GetCommitDetail(sha string) *CommitDetail

type CommitEntry

type CommitEntry struct {
	SHA     string
	Message string
}

func ListBranchCommits

func ListBranchCommits(branch, base string) []CommitEntry

ListBranchCommits returns the commits on branch relative to base, newest first.

type CommitFile

type CommitFile struct {
	Path      string `json:"path"`
	Additions int    `json:"additions"`
	Deletions int    `json:"deletions"`
}

type CompletionResult

type CompletionResult struct {
	Shell      string
	ConfigFile string
	Configured bool
}

CompletionResult holds the status of shell completion configuration

func CheckCompletionConfig

func CheckCompletionConfig() CompletionResult

CheckCompletionConfig detects the current shell and checks if completion is configured

type DetailedCommit

type DetailedCommit struct {
	SHA     string `json:"sha"`
	Message string `json:"message"`
	Author  string `json:"author"`
	Date    string `json:"date"`
}

func ListBranchCommitsDetailed

func ListBranchCommitsDetailed(branch, base string) []DetailedCommit

ListBranchCommitsDetailed returns commits with author and date info.

type DriveOptions

type DriveOptions struct {
	IssueID    string
	Filter     string
	TestCmd    string
	DryRun     bool
	NoMerge    bool
	Resume     bool
	MaxRetries int
	Supervisor string
	Coder      string
}

type DriveResult

type DriveResult struct {
	IssueID  string
	Title    string
	Status   string // "done", "blocked", "no-work"
	Attempts int
	Summary  string
	Branch   string
	Messages []string
}

type FileStat

type FileStat struct {
	Status     string
	Path       string
	Insertions int
	Deletions  int
}

func ListFilesChanged

func ListFilesChanged(branch, base string) []FileStat

ListFilesChanged returns per-file stats for the branch relative to base.

type FilterOptions

type FilterOptions struct {
	Statuses []string
	Since    string
	Before   string
	Match    string
	Mine     bool
	ParentID string
	Label    string
	Assignee string
	CycleID  string
	All      bool
	Archived bool
}

FilterOptions contains parameters for filtering issues.

type InboxItem

type InboxItem struct {
	IssueID    string          `json:"issue_id"`
	IssueTitle string          `json:"issue_title"`
	Type       model.EventType `json:"type"`
	Payload    interface{}     `json:"payload"`
	CreatedAt  time.Time       `json:"created_at"`
	CreatedBy  string          `json:"created_by"`
	OnBehalfOf string          `json:"on_behalf_of,omitempty"`
}

InboxItem is a single event surfaced in a user's personal inbox feed.

func BuildInbox

func BuildInbox(events []model.Event, issues map[string]*model.Issue, me string, since time.Time) []InboxItem

BuildInbox filters the full event log down to events relevant to `me` that occurred after `since`, newest-first. Relevance = events on issues where the user is the creator or current assignee — plus all MERGE events (team-wide visibility). The user's own actions are excluded so the inbox shows what others did. A zero `since` returns all matching events.

type InitResult

type InitResult struct {
	Created bool
	Notes   []string
}

InitResult contains the outcome of the initialization.

func InitProject

func InitProject(force bool) (*InitResult, error)

InitProject initializes the .xpo directory structure.

type LocalTransport

type LocalTransport struct {
	Config       *config.Config
	Collapse     bool
	UserOverride string
	OnBehalfOf   string
	Source       string
}

LocalTransport implements Transport by reading and writing the local .xpo/issues.db event log directly.

func (*LocalTransport) AddArtifact

func (t *LocalTransport) AddArtifact(issueID, artifactType, filename, content string) error

AddArtifact writes a generic artifact for an issue. Reserved filenames (spec.md, walkthrough.md) are rejected — use WriteSpec/WriteWalkthrough instead.

func (*LocalTransport) AddComment

func (t *LocalTransport) AddComment(issueID, text string) error

AddComment adds a comment to an issue.

func (*LocalTransport) AddIssue

func (t *LocalTransport) AddIssue(payload model.CreatePayload) (*model.Issue, error)

AddIssue creates a new issue and persists it. If payload.Status is empty, the issue defaults to BACKLOG. Side-effect rules (auto-progress, blocked_by checks) are not run on create — they apply only to subsequent updates.

func (*LocalTransport) CheckDuplicates

func (t *LocalTransport) CheckDuplicates(title string) ([]*model.Issue, error)

CheckDuplicates searches for issues with similar titles.

func (*LocalTransport) DeleteArtifact

func (t *LocalTransport) DeleteArtifact(issueID, filename string) error

DeleteArtifact removes an artifact from disk and records a "deleted" ARTIFACT event so the projection drops it from Issue.Artifacts.

func (*LocalTransport) DeleteIssue

func (t *LocalTransport) DeleteIssue(id string, reason string, cascade bool) error

DeleteIssue deletes an issue by appending a delete event. When cascade is true, children are also deleted; otherwise their ParentID is cleared.

func (*LocalTransport) DeleteSpec

func (t *LocalTransport) DeleteSpec(issueID string) error

func (*LocalTransport) DeleteWalkthrough

func (t *LocalTransport) DeleteWalkthrough(issueID string) error

func (*LocalTransport) FindIssue

func (t *LocalTransport) FindIssue(id string) (*model.Issue, []*model.Issue, bool, error)

FindIssue retrieves an issue by ID, checking the active store first, then the archive.

func (*LocalTransport) GetInbox

func (t *LocalTransport) GetInbox(since time.Time) ([]InboxItem, error)

GetInbox scans the local event log for events relevant to the current user since the given cursor.

func (*LocalTransport) GetIssue

func (t *LocalTransport) GetIssue(id string) (*model.Issue, error)

GetIssue retrieves an issue by ID, resolving short IDs if necessary.

func (*LocalTransport) GetUser

func (t *LocalTransport) GetUser() string

GetUser returns the recorded author for new events. Resolution order: caller-provided override (used by MCP for agent identity), configured user, then git config as a last resort.

func (*LocalTransport) ListArtifacts

func (t *LocalTransport) ListArtifacts(issueID string) ([]model.ArtifactSummary, error)

ListArtifacts returns the current artifacts attached to an issue, as derived from the projection.

func (*LocalTransport) ListIssues

func (t *LocalTransport) ListIssues(opts FilterOptions) ([]*model.Issue, error)

ListIssues retrieves and filters issues based on options.

func (*LocalTransport) ReadArtifact

func (t *LocalTransport) ReadArtifact(issueID, filename string) (string, error)

ReadArtifact returns the contents of an artifact for an issue.

func (*LocalTransport) ReadSpec

func (t *LocalTransport) ReadSpec(issueID string) (string, error)

func (*LocalTransport) ReadWalkthrough

func (t *LocalTransport) ReadWalkthrough(issueID string) (string, error)

func (*LocalTransport) UpdateIssue

func (t *LocalTransport) UpdateIssue(id string, payload model.UpdatePayload, action string) ([]string, error)

UpdateIssue updates an issue, handles side effects, and optionally creates a git commit when AutoCommit is enabled.

func (*LocalTransport) WriteSpec

func (t *LocalTransport) WriteSpec(issueID, content string) error

func (*LocalTransport) WriteWalkthrough

func (t *LocalTransport) WriteWalkthrough(issueID, content string) error

type MCPConfigStatus

type MCPConfigStatus struct {
	Exists         bool
	HasExponential bool
}

MCPConfigStatus describes the state of .mcp.json in the project root.

func DetectMCPConfig

func DetectMCPConfig() MCPConfigStatus

DetectMCPConfig checks whether .mcp.json exists and contains a xpo entry.

type MergeOptions

type MergeOptions struct {
	Strategy      MergeStrategy
	CommitMessage string
	DeleteBranch  bool
	KeepBranch    bool
}

type MergeResult

type MergeResult struct {
	MergeSHA string
	Messages []string
}

type MergeStrategy

type MergeStrategy string
const (
	MergeStrategyMerge  MergeStrategy = "merge"
	MergeStrategySquash MergeStrategy = "squash"
	MergeStrategyFF     MergeStrategy = "ff"
)

type RemoteTransport

type RemoteTransport struct {
	BaseURL    string
	Token      string
	User       string
	HTTPClient *http.Client
}

RemoteTransport implements Transport by proxying to a xpo server over its existing REST API.

func (*RemoteTransport) AddArtifact

func (t *RemoteTransport) AddArtifact(issueID, artifactType, filename, content string) error

func (*RemoteTransport) AddComment

func (r *RemoteTransport) AddComment(issueID, text string) error

func (*RemoteTransport) AddIssue

func (r *RemoteTransport) AddIssue(payload model.CreatePayload) (*model.Issue, error)

func (*RemoteTransport) CheckDuplicates

func (r *RemoteTransport) CheckDuplicates(title string) ([]*model.Issue, error)

func (*RemoteTransport) DeleteArtifact

func (t *RemoteTransport) DeleteArtifact(issueID, filename string) error

func (*RemoteTransport) DeleteIssue

func (r *RemoteTransport) DeleteIssue(id string, reason string, cascade bool) error

func (*RemoteTransport) DeleteSpec

func (t *RemoteTransport) DeleteSpec(issueID string) error

func (*RemoteTransport) DeleteWalkthrough

func (t *RemoteTransport) DeleteWalkthrough(issueID string) error

func (*RemoteTransport) FindIssue

func (r *RemoteTransport) FindIssue(id string) (*model.Issue, []*model.Issue, bool, error)

func (*RemoteTransport) GetInbox

func (r *RemoteTransport) GetInbox(since time.Time) ([]InboxItem, error)

GetInbox fetches the authenticated user's inbox from the server, which filters events server-side by identity and the optional since cursor.

func (*RemoteTransport) GetIssue

func (r *RemoteTransport) GetIssue(id string) (*model.Issue, error)

func (*RemoteTransport) GetUser

func (r *RemoteTransport) GetUser() string

func (*RemoteTransport) ListArtifacts

func (t *RemoteTransport) ListArtifacts(issueID string) ([]model.ArtifactSummary, error)

func (*RemoteTransport) ListIssues

func (r *RemoteTransport) ListIssues(opts FilterOptions) ([]*model.Issue, error)

func (*RemoteTransport) ReadArtifact

func (t *RemoteTransport) ReadArtifact(issueID, filename string) (string, error)

func (*RemoteTransport) ReadSpec

func (t *RemoteTransport) ReadSpec(issueID string) (string, error)

func (*RemoteTransport) ReadWalkthrough

func (t *RemoteTransport) ReadWalkthrough(issueID string) (string, error)

func (*RemoteTransport) UpdateIssue

func (r *RemoteTransport) UpdateIssue(id string, payload model.UpdatePayload, action string) ([]string, error)

func (*RemoteTransport) WriteSpec

func (t *RemoteTransport) WriteSpec(issueID, content string) error

func (*RemoteTransport) WriteWalkthrough

func (t *RemoteTransport) WriteWalkthrough(issueID, content string) error

type TimelineEntry

type TimelineEntry struct {
	Kind       string      `json:"kind"`
	Timestamp  time.Time   `json:"timestamp"`
	IssueID    string      `json:"issue_id,omitempty"`
	IssueTitle string      `json:"issue_title,omitempty"`
	EventType  string      `json:"event_type,omitempty"`
	Payload    interface{} `json:"payload,omitempty"`
	CreatedBy  string      `json:"created_by,omitempty"`
	OnBehalfOf string      `json:"on_behalf_of,omitempty"`
	Source     string      `json:"source,omitempty"`
	SHA        string      `json:"sha,omitempty"`
	Message    string      `json:"message,omitempty"`
	Author     string      `json:"author,omitempty"`
	Branch     string      `json:"branch,omitempty"`
}

func BuildTimeline

func BuildTimeline(events []model.Event, issues map[string]*model.Issue, limit int, kindFilter string) []TimelineEntry

type Transport

type Transport interface {
	ListIssues(opts FilterOptions) ([]*model.Issue, error)
	GetIssue(id string) (*model.Issue, error)
	FindIssue(id string) (issue *model.Issue, children []*model.Issue, archived bool, err error)
	AddIssue(payload model.CreatePayload) (*model.Issue, error)
	UpdateIssue(id string, payload model.UpdatePayload, action string) ([]string, error)
	AddComment(issueID, text string) error
	DeleteIssue(id string, reason string, cascade bool) error
	CheckDuplicates(title string) ([]*model.Issue, error)
	GetUser() string
	GetInbox(since time.Time) ([]InboxItem, error)

	// Artifacts
	AddArtifact(issueID, artifactType, filename, content string) error
	ReadArtifact(issueID, filename string) (string, error)
	DeleteArtifact(issueID, filename string) error
	ListArtifacts(issueID string) ([]model.ArtifactSummary, error)
	WriteSpec(issueID, content string) error
	ReadSpec(issueID string) (string, error)
	DeleteSpec(issueID string) error
	WriteWalkthrough(issueID, content string) error
	ReadWalkthrough(issueID string) (string, error)
	DeleteWalkthrough(issueID string) error
}

Transport abstracts issue data operations so the Client can work in both local mode (reading .xpo/issues.db directly) and remote mode (proxying to an exponential server over HTTP).

Jump to

Keyboard shortcuts

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