devflow

package module
v0.4.98 Latest Latest
Warning

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

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 29 Imported by: 0

README

DevFlow

Complete Go development automation: project init, testing, versioning, updates, and backups. Single-line output optimized for AI agents and terminals.

Commands

  • gonew - Initialize new Go projects
  • gotest - Run tests, vet, race detection, coverage and badges
  • gopush - Automated publish workflow: test + push + update dependents
  • devbackup - Configure and execute automated backups
  • badges - Generate SVG badges for README (test status, coverage, etc.)
  • goinstall - Install all devflow commands at once
  • codejob - Send coding tasks to AI agents (Jules, etc.)

Configuration

  • GitHub Auth - Configure GitHub authentication (OAuth, tokens, multi-account)

Roadmap

Installation

# Install all commands at once (includes codejob and all other tools)
go install webtyp.com/devflow/cmd/goinstall@latest && goinstall

Or install a single command — see each tool's doc linked above.

Features

  • Transactional CodeJob - Branch switching is atomic; state (PLAN.md, .env) is only mutated after a verified checkout.
  • Intelligent push - Auto-pulls with --rebase on non-fast-forward rejection
  • Zero config - Auto-detects tests, project structure, WASM environments
  • Minimal output - Single-line summaries for terminals and LLMs
  • Smart versioning - Auto-increments tags, skips duplicates
  • Multi-account - Switch GitHub orgs easily (cdvelop, veltylabs, webtyp)
  • Dependency updates - Auto-updates dependent modules in workspace
  • Full testing - Combines vet, tests, race detection, and exact weighted coverage across all packages

License

MIT

Documentation

Index

Constants

View Source
const (
	// DefaultIssuePromptPath is the conventional location for the task description file.
	DefaultIssuePromptPath = "docs/PLAN.md"

	// CodejobStashMessage is the label for the git stash created during branch switching.
	CodejobStashMessage = "codejob: local drift before review"

	// HintManualCheckout is the message shown when automatic branch switch fails.
	HintManualCheckout = "⚠️  Could not switch branch automatically — run manually:"
	// HintManualPRCheckout is the message shown when PR branch resolution fails.
	HintManualPRCheckout = "⚠️  Could not resolve branch from PR — switch manually:"
)
View Source
const (
	ColorRed    = "\033[0;31m"
	ColorGreen  = "\033[0;32m"
	ColorYellow = "\033[0;33m"
	ColorCyan   = "\033[0;36m"
	ColorNone   = "\033[0m"
)
View Source
const (
	FrontmatterKeyPlan          = "PLAN"           // required: commit message used when closing the loop
	FrontmatterKeyTag           = "TAG"            // optional: explicit version tag
	FrontmatterKeyExecutor      = "EXECUTOR"       // optional: executor agent
	FrontmatterKeyReviewer      = "REVIEWER"       // optional: reviewer agent
	FrontmatterKeyCorrector     = "CORRECTOR"      // optional: corrector agent
	FrontmatterKeyReviewGuide   = "REVIEW_GUIDE"   // optional: path to review guidelines
	FrontmatterKeyStatus        = "STATUS"         // optional: orchestrator status
	FrontmatterKeySession       = "SESSION"        // optional: executor session ID
	FrontmatterKeyReviewSession = "REVIEW_SESSION" // optional: reviewer session ID
	FrontmatterKeyRound         = "ROUND"          // optional: round count
	FrontmatterKeyPR            = "PR"             // optional: pull request URL
)
View Source
const (
	CascadeStatusPublished = "published"
	CascadeStatusDepsOnly  = "deps only"
	CascadeStatusSkipped   = "skipped"
	CascadeStatusFailed    = "failed"
)

CascadeStatus constants represent the possible outcomes of a module update in a cascade.

View Source
const DevFlowRepository = "webtyp.com/devflow"
View Source
const ErrPushBlockedActiveCodejob = "" /* 160-byte string literal not displayed */

ErrPushBlockedActiveCodejob is returned by Push when the repo has an active codejob session: publishing would move the base branch under the agent.

View Source
const JulesResultPrefix = "jules: "

JulesResultPrefix is the prefix on the string returned by the Jules driver after dispatching a session. cmd/codejob uses it to format the output.

View Source
const MaxCascadeDepth = 10

Variables

View Source
var (
	ErrFrontmatterMissing  = errors.New("plan frontmatter: file must start with a '---' line" + frontmatterHelp)
	ErrFrontmatterUnclosed = errors.New("plan frontmatter: opening '---' has no matching closing '---'" + frontmatterHelp)
	ErrFrontmatterNoPlan   = errors.New("plan frontmatter: missing required 'PLAN:' field (the old 'message:' key was renamed — rename it in your plan)" + frontmatterHelp)
)
View Source
var ErrNoCloseLoopMessage = errors.New("no close-loop commit message: pass one on the CLI or add 'PLAN:' to the plan frontmatter")
View Source
var GoTestArgsModel = model.Definition{
	Name: "go_test_args",
	Fields: model.Fields{
		{Name: "run", Type: model.Text()},
	},
}
View Source
var GoTestCmdFn = testCommand

GoTestCmdFn creates the command used to run 'go test'. Override in tests to avoid launching a real nested go test subprocess (e.g. from Release→Push→Test).

Functions

func CheckoutPRBranch

func CheckoutPRBranch(runner gitmod.Runner, prURL string) (string, error)

CheckoutPRBranch fetches and hard-positions the working tree on the PR's head branch. A dirty working tree is handled, not feared: local drift is stashed with a labeled stash (CodejobStashMessage) and re-applied after the switch; if re-applying conflicts, the stash is KEPT, the conflict files are listed, and an error is returned. Returns the branch name on success.

func EvaluateTestResults

func EvaluateTestResults(err error, output, moduleName string, msgs []string, skipRace bool) (testStatus, raceStatus string, stdTestsRan bool, newMsgs []string)

EvaluateTestResults analyzes the output of go test and decides the outcome This function is pure and can be easily tested.

func FindProjectRoot

func FindProjectRoot(startDir string) (string, error)

FindProjectRoot looks for go.mod in startDir or its immediate parent. Returns the absolute path to the directory containing go.mod, or an empty string and error if not found.

func FindSlowestTest

func FindSlowestTest(output string, threshold float64) (string, float64)

FindSlowestTest parses -v test output and returns the name and duration of the slowest individual test across all packages if it exceeds the specified threshold.

func FindTimedOutTests

func FindTimedOutTests(output string) []string

FindTimedOutTests parses go test output and extracts test names that timed out. Handles two scenarios: 1. Go's native timeout: "panic: test timed out after Ns\n running tests:\n TestName (Ns)" 2. Process killed externally (context.WithTimeout): finds the last "=== RUN" without a matching "--- PASS/FAIL"

func GenerateGitignore

func GenerateGitignore(targetDir string) error

GenerateGitignore generates .gitignore for Go

func GenerateHandlerFile

func GenerateHandlerFile(repoName, targetDir string) error

GenerateHandlerFile generates the main handler file

func GenerateLicense

func GenerateLicense(ownerName, targetDir string) error

GenerateLicense generates LICENSE (MIT)

func GenerateREADME

func GenerateREADME(repoName, description, targetDir string) error

GenerateREADME generates README.md

func GetBadgeColor

func GetBadgeColor(typ, value string) string

func GetGoVersion

func GetGoVersion() string

func HandleDone

func HandleDone(runner gitmod.Runner, env *DotEnv, git *gitmod.Git, prURL string) error

HandleDone executes cleanup when Jules completes: HandleDone is kept for signature compatibility but is a no-op under the new PLAN.md state model.

func HasTimeoutFlag

func HasTimeoutFlag(args []string) bool

HasTimeoutFlag checks if -timeout is already present in the args

func HasVFlag

func HasVFlag(args []string) bool

HasVFlag checks if -v is already present in the args

func InitCodejobAction

func InitCodejobAction(force bool, org, visibility string) error

InitCodejobAction scaffolds the GitHub Actions workflow at .github/workflows/codejob.yml and registers the JULES_API_KEY and GH_TOKEN secrets in the repository or organization.

func IsEnvironmentValid

func IsEnvironmentValid(dotenvPath string) bool

IsEnvironmentValid reports whether the current working directory has an active codejob context: a PLAN.md to dispatch or a session in progress.

func JulesSessionState

func JulesSessionState(sessionID, apiKey string, client HTTPClient) (msg, prURL string, done bool, err error)

JulesSessionState polls the Jules API for session status. Returns (message, prURL, isDone, error).

func KebabToCamel

func KebabToCamel(s string) string

KebabToCamel converts kebab-case or snake_case to CamelCase

func MergeAndPublish

func MergeAndPublish(runner gitmod.Runner, publisher Publisher, message, overrideTag string) (gitmod.PushResult, error)

MergeAndPublish merges the Jules PR, pulls the merged commit, and publishes via gopush.

func MergePR

func MergePR(runner gitmod.Runner) error

MergePR merges the Jules PR and deletes PLAN.md.

func ParseCLIArgs

func ParseCLIArgs(args []string) (message, tag string, isHelp, isRelease bool)

ParseCLIArgs parses command line arguments for devflow tools (codejob, gopush). It returns the message, tag, whether help was requested, and whether -release flag is present. Flags like -release and --release are detected and excluded from message/tag assignment.

func ParseCodeJobArgs

func ParseCodeJobArgs(args []string) (message, tag string, isHelp, isRelease, isResetGHToken bool)

ParseCodeJobArgs parses codejob CLI: codejob [message] [tag] [--reset-gh-token] Returns message, tag, isHelp, isRelease, and isResetGHToken.

func ParseVerifyError

func ParseVerifyError(output string) (string, bool)

ParseVerifyError detects known go mod verify failure patterns and returns an actionable message.

func ParseWasmTestPackages

func ParseWasmTestPackages(goListOut string) []string

ParseWasmTestPackages keeps the packages that have tests AND can be built for wasm.

func PrintError

func PrintError(msg string)

PrintError prints an error message in red.

func PrintInfo

func PrintInfo(msg string)

PrintInfo prints an informational message in cyan.

func PrintSuccess

func PrintSuccess(msg string)

PrintSuccess prints a success message in green.

func PrintWarning

func PrintWarning(msg string)

PrintWarning prints a warning message in yellow.

func ResolvePublishMessage

func ResolvePublishMessage(cliMessage, cliTag string, meta PlanMeta) (message, tag string, err error)

ResolvePublishMessage picks the effective close-loop commit message and tag: an explicit CLI value wins; otherwise the plan frontmatter is used.

func SerializeFrontmatter

func SerializeFrontmatter(meta PlanMeta) string

SerializeFrontmatter serializes PlanMeta back to a YAML-like frontmatter string block.

func ShouldEnableWasm

func ShouldEnableWasm(nativeOut, wasmOut string) bool

ShouldEnableWasm decides if WASM tests should be run based on go list output differences

func SplitMarkdown

func SplitMarkdown(content string) (map[string]string, string, error)

SplitMarkdown splits a markdown content into frontmatter keys and body.

func ValidateDescription

func ValidateDescription(desc string) error

ValidateDescription validates the repository description

func ValidateRepoName

func ValidateRepoName(name string) error

ValidateRepoName validates the repository name Only alphanumeric, dash, and underscore allowed

func WritePlanMeta

func WritePlanMeta(path string, meta PlanMeta) error

WritePlanMeta updates or creates a PLAN.md file at path with the given meta, preserving the markdown body.

Types

type AddRemoteCLIOpts

type AddRemoteCLIOpts struct {
	ProjectPath string
	Owner       string
	Visibility  string
}

AddRemoteCLIOpts holds parsed options for the gonew add-remote CLI.

func ParseAddRemoteArgs

func ParseAddRemoteArgs(args []string) (AddRemoteCLIOpts, error)

ParseAddRemoteArgs parses `gonew add-remote <project-path> [flags]`.

type BackupRunner

type BackupRunner interface {
	SetLog(fn func(...any))
	SetCommand(command string) error
	GetCommand() (string, error)
	Run() (string, error)
}

BackupRunner defines the interface for backup operations. Allows mocking in tests to prevent real backup execution.

type Badge

type Badge struct {
	Label string // The text displayed on the left side of the badge.
	Value string // The text displayed on the right side of the badge.
	Color string // The background color for the value part of the badge (e.g., "#4c1" or "green").
}

Badge represents a single badge with a label, value, and color. This is the primary struct used to define a badge's appearance and content.

For example, to create a "Go version" badge, you might use:

b := Badge{
  Label: "Go",
  Value: "1.18",
  Color: "#007d9c",
}

type Badges

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

Badges is responsible for creating and managing a collection of badges. It handles parsing input arguments, generating the SVG image, and preparing the necessary markdown to embed the badges in a file.

func NewBadges

func NewBadges(args ...string) *Badges

NewBadges creates and initializes a new Badges handler.

func (*Badges) BadgeMarkdown

func (h *Badges) BadgeMarkdown() string

BadgeMarkdown generates the markdown snippet for embedding the badge image.

func (*Badges) BuildBadges

func (h *Badges) BuildBadges() ([]string, error)

BuildBadges generates the SVG image, writes it to the specified output file, and returns a slice of strings intended for updating a markdown file.

func (*Badges) Err

func (h *Badges) Err() error

Err returns any error that occurred during the initialization or processing

func (*Badges) GenerateSVG

func (h *Badges) GenerateSVG() ([]byte, int, error)

GenerateSVG creates an SVG image from the configured badges.

It returns the SVG content as a byte slice, the number of badges included, and an error if the generation fails. This method is typically called by BuildBadges, but it can be used directly if you only need the SVG data.

Example of a generated SVG for two badges ("License:MIT:blue" and "Go:1.22:blue"):

<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" width="168" height="20" viewBox="0 0 168 20">

<!-- Badge: License -->
<g transform="translate(0, 0)">
  <rect x="0" y="0" width="58" height="20" fill="#6c757d"/>
  <rect x="58" y="0" width="46" height="20" fill="blue"/>
  <text x="29" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">License</text>
  <text x="81" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">MIT</text>
</g>
<!-- Badge: Go -->
<g transform="translate(109, 0)">
  <rect x="0" y="0" width="34" height="20" fill="#6c757d"/>
  <rect x="34" y="0" width="25" height="20" fill="blue"/>
  <text x="17" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">Go</text>
  <text x="46" y="14" text-anchor="middle" font-family="sans-serif" font-size="11" fill="white">1.22</text>
</g>

</svg>

func (*Badges) GoHandler

func (h *Badges) GoHandler() *Go

GoHandler returns the internal Go handler

func (*Badges) OutputFile

func (h *Badges) OutputFile() string

OutputFile returns the configured path for the output SVG file.

func (*Badges) ReadmeFile

func (h *Badges) ReadmeFile() string

ReadmeFile returns the configured path for the markdown file to be updated.

func (*Badges) SetLog

func (h *Badges) SetLog(fn func(...any))

SetLog sets the logger function

func (*Badges) SetRootDir

func (h *Badges) SetRootDir(dir string)

SetRootDir sets the root directory for badge operations

func (*Badges) UpdateBadges

func (h *Badges) UpdateBadges(readmeFile, licenseType, goVer, testStatus, coveragePercent, raceStatus, vetStatus string, quiet bool) error

UpdateBadges generates badge SVG and updates the README using the provided values.

func (*Badges) UpdateReadme

func (h *Badges) UpdateReadme() error

UpdateReadme updates the README file with the badge image line. Detects and migrates old START_SECTION/END_SECTION markers, replaces existing badge img lines, or inserts a new one.

type Bashrc

type Bashrc struct {
	FilePath string
}

Bashrc handles updates to .bashrc file using markers

func NewBashrc

func NewBashrc() *Bashrc

NewBashrc creates a new Bashrc handler for ~/.bashrc

func (*Bashrc) ExtractValue

func (b *Bashrc) ExtractValue(exportLine, key string) (string, error)

ExtractValue extracts value from export statement Input: export KEY="value" or export KEY=value Output: value

func (*Bashrc) Get

func (b *Bashrc) Get(key string) (string, error)

Get reads a variable value from .bashrc file

func (*Bashrc) Set

func (b *Bashrc) Set(key, value string) error

Set updates or creates a variable in .bashrc If value is empty, removes the variable

type CascadeEntry

type CascadeEntry struct {
	ModulePath string
	Status     string
	Detail     string
}

CascadeEntry represents the result for a single module in the cascade

type CascadeNode

type CascadeNode struct {
	Dir        string
	ModulePath string
	DependsOn  []string // List of ModulePaths this node depends on *within the cascade*
}

CascadeNode represents a module in the dependency graph

type CascadeOutcome

type CascadeOutcome struct {
	Status  string // CascadeStatusPublished | CascadeStatusDepsOnly | CascadeStatusSkipped
	Version string // set only when Status == CascadeStatusPublished
	Reason  string // human-readable, e.g. "codejob session active"
}

CascadeOutcome is the typed result of processing one node. It replaces the previous convention of encoding the status inside a free-form string.

type CascadeProcessFn

type CascadeProcessFn func(node CascadeNode, bumps []gitmod.DepBump, rootCause string) (CascadeOutcome, error)

CascadeProcessFn is the signature for the function that processes a single node

type CascadeReport

type CascadeReport struct {
	Entries []CascadeEntry
}

CascadeReport contains the full report of the cascade execution

type CodeJob

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

CodeJob orchestrates sending a coding task to a chain of AI agent drivers. It validates the prompt file, then tries each driver in priority order, falling back to the next on failure.

func NewCodeJob

func NewCodeJob(drivers ...CodeJobDriver) *CodeJob

NewCodeJob creates a CodeJob with the given ordered drivers.

func (*CodeJob) GetSteps

func (c *CodeJob) GetSteps() []*wizard.Step

GetSteps for wizard

func (CodeJob) ObjectsToPublish

func (CodeJob) ObjectsToPublish(ctx gitmod.PublishContext) (gitmod.PublishAction, string)

ObjectsToPublish implements PublishObjector. It is stateless.

func (*CodeJob) Run

func (c *CodeJob) Run(message, tag string, isRelease bool) (string, error)

Run implements the unified API logic. isRelease indicates whether to create a GitHub Release after MergeAndPublish.

func (*CodeJob) RunCI

func (c *CodeJob) RunCI(phase string) error

RunCI executes a single state transition phase for the codejob orchestrator in CI.

func (*CodeJob) Send

func (c *CodeJob) Send(issuePromptPath string) (string, error)

Send validates issuePromptPath, publishes pending changes, then tries each driver in order until one succeeds. Returns an error if the file is missing, empty, the publish fails, or all drivers fail.

func (*CodeJob) SetLog

func (c *CodeJob) SetLog(fn func(...any))

SetLog sets the logging function for the orchestrator.

func (*CodeJob) SetPublisher

func (c *CodeJob) SetPublisher(p Publisher)

SetPublisher injects a Publisher for close-loop operations.

func (*CodeJob) SetReleaser

func (c *CodeJob) SetReleaser(fn func(tag string) error)

SetReleaser injects a release function to be called after MergeAndPublish when -release flag is used.

func (*CodeJob) SetRunner

func (c *CodeJob) SetRunner(r gitmod.Runner)

SetRunner sets the command runner (mainly for testing).

type CodeJobCLIOpts

type CodeJobCLIOpts struct {
	Message        string
	Tag            string
	IsHelp         bool
	IsRelease      bool
	IsResetGHToken bool
	CIPhase        string // "dispatch", "review", "verdict", "publish"
	InitAction     bool
	Force          bool
	Org            string
	Visibility     string
}

CodeJobCLIOpts holds parsed options for the codejob CLI.

func ParseCodeJobFlags

func ParseCodeJobFlags(args []string) CodeJobCLIOpts

ParseCodeJobFlags parses the complete set of flags and positional arguments for the codejob CLI.

type CodeJobDriver

type CodeJobDriver interface {
	Name() string
	SetLog(fn func(...any))
	Send(prompt, title string) (string, error)
}

CodeJobDriver defines the contract for an external AI coding agent. Implementations: JulesDriver, (future: OllamaDriver, etc.) title is the human-readable job name (e.g. "owner/repo"), derived by CodeJob.

type CodejobPhase

type CodejobPhase string
const (
	PhaseRunning CodejobPhase = "running"
	PhaseReview  CodejobPhase = "review"
)

func CodejobPhaseOf

func CodejobPhaseOf(dir string) CodejobPhase

CodejobPhaseOf reports the current codejob phase for the given directory.

type ConsoleFilter

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

func NewConsoleFilter

func NewConsoleFilter(output func(string)) *ConsoleFilter

func (*ConsoleFilter) Add

func (cf *ConsoleFilter) Add(input string)

func (*ConsoleFilter) Flush

func (cf *ConsoleFilter) Flush()

type DevBackup

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

DevBackup handles backup operations

func NewDevBackup

func NewDevBackup() *DevBackup

NewDevBackup creates a new DevBackup instance

func (*DevBackup) GetCommand

func (d *DevBackup) GetCommand() (string, error)

GetCommand retrieves the backup command First checks environment variable, then falls back to .bashrc

func (*DevBackup) Run

func (d *DevBackup) Run() (string, error)

Run executes the backup command asynchronously Returns a message for the summary or empty string if not configured

func (*DevBackup) SetCommand

func (d *DevBackup) SetCommand(command string) error

SetCommand sets the backup command in .bashrc and current environment

func (*DevBackup) SetLog

func (d *DevBackup) SetLog(fn func(...any))

SetLog sets the logger function

type DotEnv

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

DotEnv handles .env files while trying to preserve non-key=value lines (comments, empty lines).

func NewDotEnv

func NewDotEnv(path string) *DotEnv

NewDotEnv creates a new .env handler.

func (*DotEnv) Delete

func (e *DotEnv) Delete(key string) error

Delete removes a key from the .env file.

func (*DotEnv) Get

func (e *DotEnv) Get(key string) (string, bool)

Get retrieves a value from the .env file.

func (*DotEnv) Set

func (e *DotEnv) Set(key, value string) error

Set sets or updates a value in the .env file, preserving other lines.

type FolderWatcher

type FolderWatcher interface {
	AddDirectoriesToWatch(paths ...string) error
	RemoveDirectoriesFromWatcher(paths ...string) error
}

FolderWatcher defines interface for adding/removing directories to watch

type Future

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

Future holds the async result of any initialization. It uses any (interface{}) for flexibility without generic syntax.

func NewFuture

func NewFuture(initFn func() (any, error)) *Future

NewFuture starts async initialization with the given function.

func NewResolvedFuture

func NewResolvedFuture(value any) *Future

NewResolvedFuture creates a Future that is already resolved with the given value. Useful for tests or when the value is already available synchronously.

func (*Future) Get

func (f *Future) Get() (any, error)

Get blocks until initialization completes and returns the result.

func (*Future) Ready

func (f *Future) Ready() <-chan bool

Ready returns a channel that signals completion.

type Go

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

Go handler for Go operations

func NewGo

func NewGo(gitHandler gitmod.GitClient) (*Go, error)

NewGo creates a new Go handler and verifies Go installation

func (*Go) AddPublishObjector

func (g *Go) AddPublishObjector(obj gitmod.PublishObjector)

AddPublishObjector appends an extra publish objector.

func (*Go) BuildDependentGraph

func (g *Go) BuildDependentGraph(rootModule, searchPath string) ([]CascadeNode, error)

BuildDependentGraph finds all modules that transitively depend on rootModule. It returns them in topological order.

func (*Go) DetectGoExecutable

func (g *Go) DetectGoExecutable() (string, error)

DetectGoExecutable returns the path to the go executable

func (*Go) FindDependentModules

func (g *Go) FindDependentModules(modulePath, searchPath string) ([]string, error)

FindDependentModules searches for modules that have modulePath as dependency. It excludes modules located inside the current project's root directory.

func (*Go) GetCurrentVersion

func (g *Go) GetCurrentVersion(moduleDir, dependencyPath string) (string, error)

GetCurrentVersion returns the current version of a dependency in a module

func (*Go) GetGit

func (g *Go) GetGit() gitmod.GitClient

GetGit returns the git client

func (*Go) GetLog

func (g *Go) GetLog() func(...any)

GetLog returns the logger function

func (*Go) GetModulePath

func (g *Go) GetModulePath() (string, error)

GetModulePath gets full module path

func (*Go) GoVersion

func (g *Go) GoVersion() (string, error)

GoVersion reads the Go version from the go.mod file in the current directory. It returns the version string (e.g., "1.18") or an empty string if not found.

func (*Go) HasDependency

func (g *Go) HasDependency(gomodPath, modulePath string) bool

HasDependency checks if a go.mod contains a specific dependency

func (*Go) Install

func (g *Go) Install(version string) error

Install builds and installs all commands in the cmd/ directory It injects the version using ldflags if provided

func (*Go) ModExists

func (g *Go) ModExists() bool

ModExists checks if go.mod exists

func (*Go) ModExistsInCurrentOrParent

func (g *Go) ModExistsInCurrentOrParent() bool

ModExistsInCurrentOrParent checks if go.mod exists in the rootDir or one directory up.

func (*Go) ModInit

func (g *Go) ModInit(modulePath, targetDir string) error

ModInit initializes a new go module

func (*Go) Publish

func (g *Go) Publish(message, tag string, skipTests, skipRace, skipDependents, skipBackup, skipTag, skipVerify bool) (gitmod.PushResult, error)

Publish satisfies the Publisher interface

func (*Go) Push

func (g *Go) Push(message, tag string, skipTests, skipRace, skipDependents, skipBackup, skipTag, skipVerify bool, searchPath string) (gitmod.PushResult, error)

Push executes the complete workflow for Go projects Parameters:

message: Commit message
tag: Optional tag
skipTests: If true, skips tests
skipRace: If true, skips race tests
skipDependents: If true, skips updating dependent modules
skipBackup: If true, skips backup
skipTag: If true, skips tag generation and pushes without tags
searchPath: Path to search for dependent modules (default: "..")

func (*Go) RunCascade

func (g *Go) RunCascade(rootModule, rootVersion, rootCause, searchPath string) CascadeReport

RunCascade executes the topological cascade

func (*Go) SetBackup

func (g *Go) SetBackup(b BackupRunner)

SetBackup replaces the backup runner (used in tests to inject a mock).

func (*Go) SetCascadeProcessFn

func (g *Go) SetCascadeProcessFn(fn CascadeProcessFn)

SetCascadeProcessFn sets the function used to process each node in the cascade. This is used to inject mocks in tests.

func (*Go) SetConsoleOutput

func (g *Go) SetConsoleOutput(fn func(string))

SetConsoleOutput sets the function for console output (used by ConsoleFilter)

func (*Go) SetLog

func (g *Go) SetLog(fn func(...any))

SetLog sets the logger function

func (*Go) SetPublishObjectors

func (g *Go) SetPublishObjectors(objs ...gitmod.PublishObjector)

SetPublishObjectors replaces the extra publish objectors.

func (*Go) SetRetryConfig

func (g *Go) SetRetryConfig(delay time.Duration, attempts int)

SetRetryConfig sets the retry configuration for network operations

func (*Go) SetRootDir

func (g *Go) SetRootDir(path string)

SetRootDir sets the root directory for Go operations

func (*Go) SetSumDBClient

func (g *Go) SetSumDBClient(c gitmod.SumDBClient)

SetSumDBClient enables the public-checksum-database guard before tagging. nil (never called) preserves the exact behavior this package had before this option existed.

func (*Go) Test

func (g *Go) Test(opts TestOptions) (string, error)

Test executes the test suite for the project. The zero TestOptions runs the full suite with -race, the default timeout, and the build cache enabled.

func (*Go) UpdateDependentModule

func (g *Go) UpdateDependentModule(depDir string, bumps []gitmod.DepBump, rootCause string) (CascadeOutcome, error)

func (*Go) UpdateDependents

func (g *Go) UpdateDependents(modulePath, version, searchPath string) error

UpdateDependents updates modules that depend on the current one

func (*Go) UpdateModule

func (g *Go) UpdateModule(moduleDir, dependency, version string) error

UpdateModule updates a specific module to a new version

func (*Go) UseTinygo

func (g *Go) UseTinygo(enabled bool)

UseTinygo makes the WASM suite compile with TinyGo.

It is opt-in because it is slow. It is also the only run that proves anything about TinyGo compatibility: the Go js/wasm backend supports the full stdlib, so the default WASM suite stays green on packages TinyGo cannot build.

func (*Go) Verify

func (g *Go) Verify() error

Verify verifies go.mod integrity

func (*Go) WaitForVersionAvailable

func (g *Go) WaitForVersionAvailable(modulePath, version string) error

WaitForVersionAvailable waits for a module version to be available on Go proxy

type GoModHandler

type GoModHandler struct {
	Lines    []string // all lines of the file
	Modified bool     // track if changes were made

	OnSSRFileChange func(moduleDir string) // called when ssr.go changes in a watched module
	// contains filtered or unexported fields
}

GoModHandler represents a parsed go.mod file and handles file events

func NewGoModHandler

func NewGoModHandler() *GoModHandler

NewGoModHandler reads and parses a go.mod file or returns an empty handler if path is empty

func (*GoModHandler) EnsureReplace

func (m *GoModHandler) EnsureReplace(modulePath, localPath string) bool

EnsureReplace ensures that a replace directive exists for the given module path pointing to the given local path. Returns true if the file was modified.

func (*GoModHandler) GetReplacePaths

func (m *GoModHandler) GetReplacePaths() ([]ReplaceEntry, error)

GetReplacePaths returns absolute paths from local replace directives. Relative paths are resolved starting from the directory containing go.mod.

func (*GoModHandler) HasOtherReplaces

func (m *GoModHandler) HasOtherReplaces(exceptModules ...string) bool

HasOtherReplaces returns true if there are replace directives other than the specified modules

func (*GoModHandler) MainInputFileRelativePath

func (g *GoModHandler) MainInputFileRelativePath() string

func (*GoModHandler) Name

func (g *GoModHandler) Name() string

func (*GoModHandler) NewFileEvent

func (g *GoModHandler) NewFileEvent(fileName, extension, filePath, event string) error

NewFileEvent handles changes to go.mod and ssr.go files in watched modules

func (*GoModHandler) ObjectsToPublish

func (m *GoModHandler) ObjectsToPublish(ctx gitmod.PublishContext) (gitmod.PublishAction, string)

func (*GoModHandler) RemoveReplace

func (m *GoModHandler) RemoveReplace(modulePath string) bool

RemoveReplace removes a replace directive for the given module. Local replace directives (target starting with "." or "/", e.g. "=> ./") are preserved: subpackages (tests/, cmd/, etc.) commonly use a self-referencing local replace to pull in the parent module without polluting the root go.mod, and that must survive dependent-module updates. Returns true if a replace was found and removed

func (*GoModHandler) RunTidy

func (m *GoModHandler) RunTidy() error

RunTidy executes 'go mod tidy' in the directory of the go.mod file

func (*GoModHandler) Save

func (m *GoModHandler) Save() error

Save writes changes back to the file if modified

func (*GoModHandler) SetFolderWatcher

func (g *GoModHandler) SetFolderWatcher(watcher FolderWatcher)

func (*GoModHandler) SetLog

func (g *GoModHandler) SetLog(fn func(messages ...any))

func (*GoModHandler) SetOnSSRFileChange

func (g *GoModHandler) SetOnSSRFileChange(fn func(string))

func (*GoModHandler) SetRootDir

func (g *GoModHandler) SetRootDir(path string)

func (*GoModHandler) SupportedExtensions

func (g *GoModHandler) SupportedExtensions() []string

func (*GoModHandler) UnobservedFiles

func (g *GoModHandler) UnobservedFiles() []string

type GoModInterface

type GoModInterface interface {
	NewFileEvent(fileName, extension, filePath, event string) error
	SetFolderWatcher(watcher FolderWatcher)
	Name() string
	SupportedExtensions() []string
	MainInputFileRelativePath() string
	UnobservedFiles() []string
	SetLog(fn func(...any))
	SetRootDir(path string)
	GetReplacePaths() ([]ReplaceEntry, error)
}

GoModInterface defines interface for go.mod handling

type GoNew

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

GoNew orchestrator

func NewGoNew

func NewGoNew(git gitmod.GitClient, github *Future, goHandler *Go) *GoNew

NewGoNew creates orchestrator (all handlers must be initialized)

func (*GoNew) AddRemote

func (gn *GoNew) AddRemote(projectPath, visibility, owner string) (string, error)

AddRemote adds GitHub remote to existing local project

func (*GoNew) Create

func (gn *GoNew) Create(opts NewProjectOptions) (string, error)

Create executes full workflow with remote (or local-only fallback)

func (*GoNew) GetSteps

func (gn *GoNew) GetSteps() []*wizard.Step

GetSteps returns the sequence of steps to create a new Go project

func (*GoNew) SetLog

func (gn *GoNew) SetLog(fn func(...any))

SetLog sets the logger function

type GoNewCLIOpts

type GoNewCLIOpts struct {
	Name        string
	Description string
	Owner       string
	Visibility  string
	LocalOnly   bool
	License     string
}

GoNewCLIOpts holds parsed options for the gonew create CLI.

func ParseGoNewArgs

func ParseGoNewArgs(args []string) (GoNewCLIOpts, error)

ParseGoNewArgs parses `gonew <repo-name> <description> [flags]`. Flags may appear before or after the positional arguments.

type GoTestArgs

type GoTestArgs struct {
	Run string
}

GoTestArgs are the arguments accepted by the run_tests MCP tool.

func (*GoTestArgs) DecodeFields

func (m *GoTestArgs) DecodeFields(r model.FieldReader)

func (*GoTestArgs) EncodeFields

func (m *GoTestArgs) EncodeFields(w model.FieldWriter)

func (*GoTestArgs) IsNil

func (m *GoTestArgs) IsNil() bool

func (*GoTestArgs) ModelName

func (m *GoTestArgs) ModelName() string

func (*GoTestArgs) Pointers

func (m *GoTestArgs) Pointers() []any

func (*GoTestArgs) Schema

func (m *GoTestArgs) Schema() []model.Field

func (*GoTestArgs) Validate

func (m *GoTestArgs) Validate(action byte) error

type GoTestProvider

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

GoTestProvider exposes the gotest suite as a single MCP tool.

func NewGoTestProvider

func NewGoTestProvider(g *Go) *GoTestProvider

NewGoTestProvider creates a new GoTestProvider.

func (*GoTestProvider) Tools

func (p *GoTestProvider) Tools() []mcp.Tool

Tools implements mcp.ToolProvider.

type HTTPClient

type HTTPClient interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPClient defines the interface for HTTP operations (injectable for tests).

type JulesAuth

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

JulesAuth manages the Jules API key via the system keyring. On first use it prompts the user to enter the key and stores it securely.

func NewJulesAuth

func NewJulesAuth() (*JulesAuth, error)

NewJulesAuth creates a JulesAuth over the "devflow" keyring service.

func (*JulesAuth) EnsureAPIKey

func (a *JulesAuth) EnsureAPIKey() (string, error)

EnsureAPIKey returns the Jules API key from the environment or keyring. If absent, prompts the user for it once and persists it.

func (*JulesAuth) HasKey

func (a *JulesAuth) HasKey() bool

HasKey returns true if the Jules API key is already stored in the environment or keyring.

func (*JulesAuth) SetLog

func (a *JulesAuth) SetLog(fn func(...any))

SetLog sets the logging function.

type JulesConfig

type JulesConfig struct {
	APIKey              string        // optional: loaded from keyring if empty
	SourceID            string        // optional: auto-detected via gh CLI if empty
	StartBranch         string        // optional: auto-detected via git if empty
	SessionTitle        string        // optional: defaults to prompt filename
	SourceIndexTimeout  time.Duration // optional: max wait for source to appear (default 2m)
	SourceIndexInterval time.Duration // optional: polling interval for source check (default 10s)
}

JulesConfig holds the configuration for the Jules driver. All fields are optional: APIKey is loaded from keyring if empty, SourceID and StartBranch are auto-detected via gh/git if empty.

type JulesDriver

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

JulesDriver implements CodeJobDriver for the Jules AI agent.

func NewJulesDriver

func NewJulesDriver(config JulesConfig) *JulesDriver

NewJulesDriver creates a JulesDriver. All JulesConfig fields are optional.

func (*JulesDriver) Name

func (d *JulesDriver) Name() string

Name returns the driver name.

func (*JulesDriver) Send

func (d *JulesDriver) Send(prompt, title string) (string, error)

Send creates a Jules session using the prompt and title resolved by CodeJob. Jules accesses the referenced file directly from the repository via its GitHub App access. If the source is not yet indexed in Jules (404 on new repos), it polls GET /sources until the source appears or the timeout is exceeded.

func (*JulesDriver) SessionID

func (d *JulesDriver) SessionID() string

SessionID returns the last session ID created.

func (*JulesDriver) SetHTTPClient

func (d *JulesDriver) SetHTTPClient(client HTTPClient)

SetHTTPClient replaces the HTTP client (for testing).

func (*JulesDriver) SetLog

func (d *JulesDriver) SetLog(fn func(...any))

SetLog sets the logging function.

func (*JulesDriver) SetRunner

func (d *JulesDriver) SetRunner(r gitmod.Runner)

SetRunner replaces the command runner used for the gh session check (for testing).

type MockDevBackup

type MockDevBackup struct {
	RunCalled     int
	RunResult     string
	RunErr        error
	CommandStored string
}

MockDevBackup is a no-op BackupRunner for use in tests.

func (*MockDevBackup) GetCommand

func (m *MockDevBackup) GetCommand() (string, error)

func (*MockDevBackup) Run

func (m *MockDevBackup) Run() (string, error)

func (*MockDevBackup) SetCommand

func (m *MockDevBackup) SetCommand(cmd string) error

func (*MockDevBackup) SetLog

func (m *MockDevBackup) SetLog(_ func(...any))

type NewProjectOptions

type NewProjectOptions struct {
	Name        string // Required, must be valid (alphanumeric, dash, underscore only)
	Description string // Required, max 350 chars
	Owner       string // GitHub owner/organization (default: detected from gh or git config)
	Visibility  string // "public" or "private" (default: "public")
	Directory   string // Supports ~/path, ./path, /abs/path (default: ./{Name})
	LocalOnly   bool   // If true, skip remote creation
	License     string // Default "MIT"
}

NewProjectOptions options for creating a new project

type PlanMeta

type PlanMeta struct {
	Message       string // required: commit message used when closing the loop
	Tag           string // optional: explicit version tag (e.g. "v0.1.0")
	Executor      string // optional: agent that implements (default "jules")
	Reviewer      string // optional: agent that reviews (default "none")
	Corrector     string // optional: agent that applies review feedback (default: executor)
	ReviewGuide   string // optional: path to extra review criteria
	Status        string // optional: dispatch -> running -> reviewing -> review
	Session       string // optional: executor session id
	ReviewSession string // optional: reviewer session id
	Round         int    // optional: executor<->reviewer round count (capped, default 3)
	PR            string // optional: URL of the PR opened by the executor
}

PlanMeta holds the parsed frontmatter of a docs/PLAN.md file.

func ParseFrontmatter

func ParseFrontmatter(content string) (PlanMeta, error)

ParseFrontmatter parses the leading frontmatter block of content and maps it to PlanMeta, requiring 'PLAN'. Structural parsing is delegated to webtyp/markdown; devflow only owns the "which keys are required" rule.

func ReadPlanMeta

func ReadPlanMeta(path string) (PlanMeta, error)

ReadPlanMeta reads and validates the frontmatter of a plan file at path.

type Publisher

type Publisher interface {
	Publish(message, tag string, skipTests, skipRace, skipDependents, skipBackup, skipTag, skipVerify bool) (git.PushResult, error)
}

Publisher defines the interface for publishing code changes.

type ReplaceEntry

type ReplaceEntry struct {
	ModulePath string // The module being replaced
	LocalPath  string // The local path replacement
}

ReplaceEntry represents a local replace directive found in go.mod

type SessionProvider

type SessionProvider interface {
	SessionID() string
}

SessionProvider is implemented by CodeJobDrivers that return a session ID after a successful Send(). CodeJob uses this to persist to .env.

type TestOptions added in v0.4.83

type TestOptions struct {
	Args     []string // extra `go test` arguments; nil or empty = full suite
	SkipRace bool     // omit -race
	Timeout  int      // seconds; 0 = the package default
	NoCache  bool     // add -count=1
	RunAll   bool     // include the packages normally skipped
}

TestOptions configures a test run. The zero value runs the full suite with the race detector, the default timeout, and the build cache enabled.

type Watchdog

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

Watchdog monitors test output for stalled tests.

func NewWatchdog

func NewWatchdog(timeout time.Duration, onKill func()) *Watchdog

NewWatchdog creates a new watchdog.

func (*Watchdog) Add

func (w *Watchdog) Add(s string)

Add appends output to be parsed.

func (*Watchdog) Culprits

func (w *Watchdog) Culprits() []string

Culprits returns the list of tests that were running when the watchdog fired.

func (*Watchdog) Start

func (w *Watchdog) Start()

Start begins the monitoring goroutine.

func (*Watchdog) Stop

func (w *Watchdog) Stop()

Stop halts the monitoring goroutine.

Directories

Path Synopsis
cmd
badges command
codejob command
devbackup command
goinstall command
gonew command
gopush command
gotest command

Jump to

Keyboard shortcuts

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