Documentation
¶
Overview ¶
Package migrate implements dotfile migration analysis and execution.
Index ¶
- func Execute(ctx context.Context, graph *op.Graph, analysis *MigrationAnalysis) (*op.Trace, error)
- func FormatMigrationExplain(ctx context.Context, w io.Writer, analysis *MigrationAnalysis, ...) error
- func FormatMigrationPlan(w io.Writer, graph *op.Graph, analysis *MigrationAnalysis, format string) error
- func RegisterLayer(ctx context.Context, sourceRoot, layerDir string, useMove, verbose bool) error
- func WriteMigratedMarker(sourceRoot string, graph *op.Graph, analysis *MigrationAnalysis) error
- type DetectedInstall
- type EncryptionSystem
- type ExecutableFile
- type GatherInput
- type InputLimits
- type LLMResult
- type MigratedMarker
- type MigrationAnalysis
- type MigrationStats
- type ModelCache
- type ModelCacheMeta
- type ModelLimits
- type Options
- type ProviderConfig
- type ProviderInfo
- type Rename
- type RepoLayer
- type ScriptAnalysis
- type SecretFinding
- type Session
- type SessionResult
- type SessionState
- type SourceSystem
- type StructureInfo
- type TreeNode
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Execute ¶
Execute runs the assembled migration graph and writes the .writ-migrated marker.
The graph executes as a graph — one op.GraphExecutor.Run over the whole assembly (the step-33 rewrite; formerly each file.move node was strip-mined into a fresh one-node graph and dispatched separately, so the assembled graph was never run). Before the run, the rename targets are conflict-checked: file.move would archive-and-overwrite an existing target, but a pre-existing target here means the tree is not in the expected pre-migration shape, so the run is refused before any node dispatches. Progress goes to stderr via the standard cli output functions.
Parameters:
- `ctx`: the cancellation context for the run.
- `graph`: the assembled migration graph.
- `analysis`: the migration analysis; supplies the source root and system label.
Returns:
- `*op.Trace`: the run's execution trace — the migration receipt, which the caller persists (nil when the graph was empty). Non-nil even when the run failed, so a failed run's journal survives.
- `error`: non-nil on a conflict, an open-root failure, a run failure, or a marker-write failure.
func FormatMigrationExplain ¶
func FormatMigrationExplain(ctx context.Context, w io.Writer, analysis *MigrationAnalysis, provider model.Provider) error
FormatMigrationExplain uses AI to generate a natural language explanation of the migration analysis. This provides a conversational summary that highlights key findings and actionable recommendations.
func FormatMigrationPlan ¶
func FormatMigrationPlan(w io.Writer, graph *op.Graph, analysis *MigrationAnalysis, format string) error
FormatMigrationPlan renders the execution Graph and MigrationAnalysis as human-readable output.
Supported formats: "text" (default), "yaml", "json" For "explain" format, use FormatMigrationExplain which requires an AI provider.
func RegisterLayer ¶
RegisterLayer registers `sourceRoot` as the layer at `layerDir` — the migrate flow's phase 5.
Its own graph and run per the settled two-run design (a failed registration must not unwind the completed restructure).
Link mode (`useMove` false, the default) symlinks `layerDir` → `sourceRoot`; move mode moves the content into `layerDir`. The [clearExistingLayer] guard runs Go-side first (remove a symlink or empty directory at the layer path; refuse a non-empty directory). The graph — `file.mkdir(<layers-parent>)` then `file.link` or `file.move`, immediate-bound — runs under a root confined at the deepest common ancestor of `sourceRoot` and `layerDir` (the settled confinement ruling: computed over the actual paths, typically `$HOME`). The run's trace persists via cli.WriteTrace, success or failure.
Parameters:
- `ctx`: the cancellation context for the run.
- `sourceRoot`: the absolute path of the migrated environment repository.
- `layerDir`: the absolute layer directory (`<layers>/<layer>`).
- `useMove`: move the content into the layer directory instead of symlinking.
- `verbose`: narrate progress via cli.Note.
Returns:
- `error`: non-nil when the guard refuses, planning fails, or the run fails.
func WriteMigratedMarker ¶
func WriteMigratedMarker(sourceRoot string, graph *op.Graph, analysis *MigrationAnalysis) error
WriteMigratedMarker writes the .writ-migrated marker file.
The marker is the human-facing record of what moved — the one legitimate graph-slot inspection left in this package's execution path (reporting; the executor owns slot resolution for dispatch).
Parameters:
- `sourceRoot`: the root directory where the marker file is written.
- `graph`: the executed migration graph; its file.move nodes supply the rename list.
- `analysis`: the migration analysis with system metadata.
Returns:
- `error`: non-nil if writing the marker fails.
Types ¶
type DetectedInstall ¶
type DetectedInstall struct {
Line int `json:"line" yaml:"line"`
Manager string `json:"manager" yaml:"manager"`
Name string `json:"name" yaml:"name"`
Command string `json:"command" yaml:"command"`
LorePackage string `json:"lore_package,omitempty" yaml:"lore_package,omitempty"`
}
DetectedInstall represents a package installation detected in a script. The LLM may populate this when analyzing scripts that contain package manager commands.
func (DetectedInstall) IsResolved ¶
func (d DetectedInstall) IsResolved() bool
IsResolved returns true if this install maps to a known lore package.
type EncryptionSystem ¶
type EncryptionSystem string
EncryptionSystem identifies the secret encryption tool in use.
const ( EncryptGitCrypt EncryptionSystem = "git-crypt" EncryptBlackbox EncryptionSystem = "blackbox" EncryptTranscrypt EncryptionSystem = "transcrypt" EncryptGPG EncryptionSystem = "gpg" EncryptAge EncryptionSystem = "age" EncryptAnsibleVault EncryptionSystem = "ansible-vault" EncryptSOPS EncryptionSystem = "sops" EncryptNone EncryptionSystem = "none" )
Encryption system constants.
type ExecutableFile ¶
type ExecutableFile struct {
Path string `json:"path"` // Relative path from root
Contents string `json:"contents"` // File contents
}
ExecutableFile holds a script's path and contents.
type GatherInput ¶
type GatherInput struct {
Root string `json:"root"` // Absolute path to source root
Tree *TreeNode `json:"tree"` // Directory structure
Executables []ExecutableFile `json:"executables"` // Scripts with contents
}
GatherInput collects tree structure and script contents for LLM analysis.
func GatherInputs ¶
func GatherInputs(root string, maxDepth, maxScriptBytes int) (*GatherInput, error)
GatherInputs walks the directory tree and collects structure and executable contents. maxDepth limits how deep to recurse (0 = root only, -1 = unlimited). maxScriptBytes limits total script content (0 = unlimited).
func (*GatherInput) FormatForPrompt ¶
func (g *GatherInput) FormatForPrompt() string
FormatForPrompt formats the gathered input for the LLM prompt.
func (*GatherInput) ToJSON ¶
func (g *GatherInput) ToJSON() ([]byte, error)
ToJSON serializes the GatherInput for the LLM prompt.
type InputLimits ¶
type InputLimits struct {
TreeDepth int `yaml:"tree_depth" json:"tree_depth"`
ScriptBudget int `yaml:"script_budget" json:"script_budget"`
}
InputLimits holds computed limits for input gathering.
func LoadInputLimits ¶
func LoadInputLimits(reg *lorepackage.Registry, aiProvider model.Provider) (InputLimits, error)
LoadInputLimits reads provider and model limits from the registry.
Resolution order: 1. Look up model in litellm-cache.json, compute limits from max_input_tokens 2. Apply provider overrides (e.g., GitHub's rate limits) 3. Fall back to provider's default input_limits if model not found
type LLMResult ¶
type LLMResult struct {
Analysis *MigrationAnalysis
Graph *op.Graph
ExecGraph registryExecutionGraph
}
LLMResult holds the parsed response from LLM analysis.
func AnalyzeWithLLMFromRegistry ¶
func AnalyzeWithLLMFromRegistry( ctx context.Context, aiProvider model.Provider, reg *lorepackage.Registry, input *GatherInput, ) (*LLMResult, error)
AnalyzeWithLLMFromRegistry sends gathered inputs to the LLM using registry-loaded prompt.
type MigratedMarker ¶
type MigratedMarker struct {
Timestamp string `yaml:"timestamp"`
System string `yaml:"system"`
Renames []Rename `yaml:"renames"`
}
MigratedMarker records what was done during execution.
type MigrationAnalysis ¶
type MigrationAnalysis struct {
// SourceRoot is the absolute path to the source repository.
SourceRoot string `json:"source_root" yaml:"source_root"`
// System is the detected source system (tuckr, stow, chezmoi, etc.).
System SourceSystem `json:"system" yaml:"system"`
// SystemConfidence is the detection confidence (0.0-1.0).
SystemConfidence float64 `json:"system_confidence,omitempty" yaml:"system_confidence,omitempty"`
// InputSummary describes what the LLM saw in the inputs.
InputSummary string `json:"input_summary,omitempty" yaml:"input_summary,omitempty"`
// Structure describes the detected repository structure.
Structure *StructureInfo `json:"structure,omitempty" yaml:"structure,omitempty"`
// RepoLayer indicates the precedence layer (base, team, personal).
RepoLayer RepoLayer `json:"repo_layer" yaml:"repo_layer"`
// EncryptionSystems lists detected encryption tools (git-crypt, sops, etc.).
EncryptionSystems []EncryptionSystem `json:"encryption_systems,omitempty" yaml:"encryption_systems,omitempty"`
// Projects lists unique project names found in the repository.
Projects []string `json:"projects,omitempty" yaml:"projects,omitempty"`
// Platforms lists unique platform names found in the repository.
Platforms []string `json:"platforms,omitempty" yaml:"platforms,omitempty"`
// Scripts contains analysis of lifecycle scripts.
Scripts []ScriptAnalysis `json:"scripts,omitempty" yaml:"scripts,omitempty"`
// SecretFindings contains detected secrets with explanations.
SecretFindings []SecretFinding `json:"secret_findings,omitempty" yaml:"secret_findings,omitempty"`
// Observations are insights about the repository structure.
Observations []string `json:"observations,omitempty" yaml:"observations,omitempty"`
// Warnings are concerns that may need attention before/after migration.
Warnings []string `json:"warnings,omitempty" yaml:"warnings,omitempty"`
// Recommendations are suggested actions after migration.
Recommendations []string `json:"recommendations,omitempty" yaml:"recommendations,omitempty"`
// Stats provides summary counts derived from the inventory.
Stats MigrationStats `json:"stats,omitempty" yaml:"stats,omitempty"`
}
MigrationAnalysis holds non-executable understanding of a source repository. This is the "why" and "what to watch out for" — separate from the execution Graph which specifies "what to do".
The execution Graph and MigrationAnalysis are produced together by BuildMigration(). FormatMigrationPlan() renders both as human-readable output.
func BuildMigration ¶
BuildMigration performs LLM-based analysis, returning an execution Graph and MigrationAnalysis.
This is the primary API that separates executable operations from non-executable understanding.
The LLM receives:
- Directory tree structure (built with Go, cross-platform)
- Contents of all executable scripts
The LLM returns:
- Analysis: system detection, structure, observations, warnings, recommendations
- Execution Graph: rename operations for directory structure changes
type MigrationStats ¶
type MigrationStats struct {
TotalFiles int `json:"total_files" yaml:"total_files"`
StaticConfigs int `json:"static_configs" yaml:"static_configs"`
Scripts int `json:"scripts" yaml:"scripts"`
LifecycleScripts int `json:"lifecycle_scripts" yaml:"lifecycle_scripts"`
Secrets int `json:"secrets" yaml:"secrets"`
Fonts int `json:"fonts" yaml:"fonts"`
Templates int `json:"templates" yaml:"templates"`
Completions int `json:"completions" yaml:"completions"`
ManPages int `json:"man_pages" yaml:"man_pages"`
Binaries int `json:"binaries" yaml:"binaries"`
Projects int `json:"projects" yaml:"projects"`
Platforms int `json:"platforms" yaml:"platforms"`
Renames int `json:"renames" yaml:"renames"`
}
MigrationStats summarizes the migration numerically.
type ModelCache ¶
type ModelCache struct {
Meta ModelCacheMeta `json:"_meta"`
Models map[string]ModelLimits `json:"models"`
}
ModelCache represents the litellm-cache.json structure.
type ModelCacheMeta ¶
type ModelCacheMeta struct {
Source string `json:"source"`
SourceFile string `json:"source_file"`
FetchedAt string `json:"fetched_at"`
}
ModelCacheMeta contains metadata about the cache.
type ModelLimits ¶
type ModelLimits struct {
MaxInputTokens int `json:"max_input_tokens"`
MaxOutputTokens int `json:"max_output_tokens"`
LiteLLMProvider string `json:"litellm_provider"`
}
ModelLimits holds limits for a specific model from LiteLLM.
type Options ¶
type Options struct {
SourceRoot string
TargetRoot string // empty = rename in place
Execute bool
Verbose bool
Format string // "json" (default), "yaml", "text"
Provider model.Provider
RegClient *lorepackage.Registry
TreeDepth int // max directory depth to scan (0 = auto based on provider)
ScriptBudget int // max bytes of script content to include (0 = auto based on provider)
}
Options controls migration behavior.
type ProviderConfig ¶
type ProviderConfig struct {
ModelCache string `yaml:"model_cache"` // Path to litellm-cache.json
Providers map[string]ProviderInfo `yaml:"providers"`
}
ProviderConfig represents the providers.yaml structure from devlore-registry.
type ProviderInfo ¶
type ProviderInfo struct {
Description string `yaml:"description"`
LiteLLMProvider string `yaml:"litellm_provider"` // Maps to litellm_provider in cache
MaxInputOverride int `yaml:"max_input_override"` // ReceiverType-enforced limit (e.g., GitHub)
MaxOutputOverride int `yaml:"max_output_override"`
InputLimits InputLimits `yaml:"input_limits"` // Default limits if model not in cache
}
ProviderInfo holds configuration for a specific provider.
type RepoLayer ¶
type RepoLayer string
RepoLayer indicates the precedence layer of a repository. Precedence: base (lowest) -> team -> personal (highest).
type ScriptAnalysis ¶
type ScriptAnalysis struct {
RelPath string `json:"rel_path" yaml:"rel_path"`
Name string `json:"name" yaml:"name"`
Phase string `json:"phase" yaml:"phase"`
PlatformGuard string `json:"platform_guard,omitempty" yaml:"platform_guard,omitempty"`
LineCount int `json:"line_count" yaml:"line_count"`
Resolved []DetectedInstall `json:"resolved,omitempty" yaml:"resolved,omitempty"`
Unresolved []DetectedInstall `json:"unresolved,omitempty" yaml:"unresolved,omitempty"`
Observations []string `json:"observations,omitempty" yaml:"observations,omitempty"`
}
ScriptAnalysis captures information extracted from a lifecycle script. In the LLM-first approach, this is populated by the LLM based on reading script contents directly.
type SecretFinding ¶
type SecretFinding struct {
RelPath string `json:"rel_path" yaml:"rel_path"`
Encryption EncryptionSystem `json:"encryption" yaml:"encryption"`
Reason string `json:"reason" yaml:"reason"`
SuggestedPattern string `json:"suggested_pattern,omitempty" yaml:"suggested_pattern,omitempty"`
}
SecretFinding represents a detected secret file.
type Session ¶
type Session struct {
// contains filtered or unexported fields
}
Session implements console.Session for interactive migration.
func NewSession ¶
NewSession creates a new migration session.
func NewSessionWithProvider ¶
func NewSessionWithProvider(opts Options, aiProvider model.Provider, regClient *lorepackage.Registry) *Session
NewSessionWithProvider creates a session with an AI provider.
type SessionResult ¶
type SessionResult struct {
Graph *op.Graph
Analysis *MigrationAnalysis
Executed bool
ReceiptPath string
}
SessionResult is the final output of a migration session.
type SessionState ¶
type SessionState int
SessionState represents a state in the migration session.
const ( StateAnalyzing SessionState = iota StateConversing StatePlanProposed StateExecuting StateComplete StateError )
StateAnalyzing through StateError represent the lifecycle states of a migration session, from initial analysis through completion or error.
type SourceSystem ¶
type SourceSystem string
SourceSystem identifies the dotfile management approach used in the source repository.
const ( SystemNative SourceSystem = "native" // Already writ-compatible (Home/ or System/) SystemTuckr SourceSystem = "tuckr" // Tuckr dotfile manager SystemStow SourceSystem = "stow" // GNU Stow SystemChezmoi SourceSystem = "chezmoi" // chezmoi SystemYadm SourceSystem = "yadm" // yadm SystemBareGit SourceSystem = "bare-git" // Bare git repo as home SystemScriptBased SourceSystem = "script-based" // Custom install scripts SystemUnknown SourceSystem = "unknown" )
Source system detection constants.
type StructureInfo ¶
type StructureInfo struct {
// GroupsPath is where groups live (e.g., "Home/Configs").
GroupsPath string `json:"groups_path" yaml:"groups_path"`
// NamingConvention is the current naming pattern (e.g., "<group>-<Platform>").
NamingConvention string `json:"naming_convention" yaml:"naming_convention"`
// Groups is the list of group names found.
Groups []string `json:"groups" yaml:"groups"`
// Platforms is the list of platform names found.
Platforms []string `json:"platforms" yaml:"platforms"`
}
StructureInfo describes the detected repository structure.
type TreeNode ¶
type TreeNode struct {
Type string `json:"type"` // "directory" or "file"
Name string `json:"name"` // File/directory name
Contents []*TreeNode `json:"contents,omitempty"` // Children (directories only)
}
TreeNode represents a directory or file in the tree structure. This mirrors the format produced by `tree -J` for LLM consumption.