summaryrefslogtreecommitdiffhomepage
path: root/internal/config
diff options
context:
space:
mode:
Diffstat (limited to 'internal/config')
-rw-r--r--internal/config/config.go266
-rw-r--r--internal/config/init.go60
2 files changed, 0 insertions, 326 deletions
diff --git a/internal/config/config.go b/internal/config/config.go
deleted file mode 100644
index 2197f5aa4..000000000
--- a/internal/config/config.go
+++ /dev/null
@@ -1,266 +0,0 @@
-// Package config manages application configuration from various sources.
-package config
-
-import (
- "encoding/json"
- "fmt"
- "log/slog"
- "os"
- "os/user"
- "path/filepath"
- "strings"
-
- "github.com/spf13/viper"
-)
-
-// Data defines storage configuration.
-type Data struct {
- Directory string `json:"directory,omitempty"`
-}
-
-// TUIConfig defines the configuration for the Terminal User Interface.
-type TUIConfig struct {
- Theme string `json:"theme,omitempty"`
- CustomTheme map[string]any `json:"customTheme,omitempty"`
-}
-
-// ShellConfig defines the configuration for the shell used by the bash tool.
-type ShellConfig struct {
- Path string `json:"path,omitempty"`
- Args []string `json:"args,omitempty"`
-}
-
-// Config is the main configuration structure for the application.
-type Config struct {
- Data Data `json:"data"`
- WorkingDir string `json:"wd,omitempty"`
- Debug bool `json:"debug,omitempty"`
- DebugLSP bool `json:"debugLSP,omitempty"`
- ContextPaths []string `json:"contextPaths,omitempty"`
- TUI TUIConfig `json:"tui"`
- Shell ShellConfig `json:"shell,omitempty"`
-}
-
-// Application constants
-const (
- defaultDataDirectory = ".opencode"
- defaultLogLevel = "info"
- appName = "opencode"
-
- MaxTokensFallbackDefault = 4096
-)
-
-var defaultContextPaths = []string{
- ".github/copilot-instructions.md",
- ".cursorrules",
- ".cursor/rules/",
- "CLAUDE.md",
- "CLAUDE.local.md",
- "CONTEXT.md",
- "CONTEXT.local.md",
- "opencode.md",
- "opencode.local.md",
- "OpenCode.md",
- "OpenCode.local.md",
- "OPENCODE.md",
- "OPENCODE.local.md",
-}
-
-// Global configuration instance
-var cfg *Config
-
-// Load initializes the configuration from environment variables and config files.
-// If debug is true, debug mode is enabled and log level is set to debug.
-// It returns an error if configuration loading fails.
-func Load(workingDir string, debug bool) (*Config, error) {
- if cfg != nil {
- return cfg, nil
- }
-
- cfg = &Config{
- WorkingDir: workingDir,
- }
-
- configureViper()
- setDefaults(debug)
-
- // Read global config
- if err := readConfig(viper.ReadInConfig()); err != nil {
- return cfg, err
- }
-
- // Load and merge local config
- mergeLocalConfig(workingDir)
-
- // Apply configuration to the struct
- if err := viper.Unmarshal(cfg); err != nil {
- return cfg, fmt.Errorf("failed to unmarshal config: %w", err)
- }
-
- defaultLevel := slog.LevelInfo
- if cfg.Debug {
- defaultLevel = slog.LevelDebug
- }
- slog.SetLogLoggerLevel(defaultLevel)
-
- // Validate configuration
- if err := Validate(); err != nil {
- return cfg, fmt.Errorf("config validation failed: %w", err)
- }
- return cfg, nil
-}
-
-// configureViper sets up viper's configuration paths and environment variables.
-func configureViper() {
- viper.SetConfigName(fmt.Sprintf(".%s", appName))
- viper.SetConfigType("json")
- viper.AddConfigPath("$HOME")
- viper.AddConfigPath(fmt.Sprintf("$XDG_CONFIG_HOME/%s", appName))
- viper.AddConfigPath(fmt.Sprintf("$HOME/.config/%s", appName))
- viper.SetEnvPrefix(strings.ToUpper(appName))
- viper.AutomaticEnv()
-}
-
-// setDefaults configures default values for configuration options.
-func setDefaults(debug bool) {
- viper.SetDefault("data.directory", defaultDataDirectory)
- viper.SetDefault("contextPaths", defaultContextPaths)
- viper.SetDefault("tui.theme", "opencode")
-
- if debug {
- viper.SetDefault("debug", true)
- viper.Set("log.level", "debug")
- } else {
- viper.SetDefault("debug", false)
- viper.SetDefault("log.level", defaultLogLevel)
- }
-}
-
-// readConfig handles the result of reading a configuration file.
-func readConfig(err error) error {
- if err == nil {
- return nil
- }
-
- // It's okay if the config file doesn't exist
- if _, ok := err.(viper.ConfigFileNotFoundError); ok {
- return nil
- }
-
- return fmt.Errorf("failed to read config: %w", err)
-}
-
-// mergeLocalConfig loads and merges configuration from the local directory.
-func mergeLocalConfig(workingDir string) {
- local := viper.New()
- local.SetConfigName(fmt.Sprintf(".%s", appName))
- local.SetConfigType("json")
- local.AddConfigPath(workingDir)
-
- // Merge local config if it exists
- if err := local.ReadInConfig(); err == nil {
- viper.MergeConfigMap(local.AllSettings())
- }
-}
-
-// Validate checks if the configuration is valid and applies defaults where needed.
-func Validate() error {
- if cfg == nil {
- return fmt.Errorf("config not loaded")
- }
-
- return nil
-}
-
-// Get returns the current configuration.
-// It's safe to call this function multiple times.
-func Get() *Config {
- return cfg
-}
-
-// WorkingDirectory returns the current working directory from the configuration.
-func WorkingDirectory() string {
- if cfg == nil {
- panic("config not loaded")
- }
- return cfg.WorkingDir
-}
-
-// GetHostname returns the system hostname or "User" if it can't be determined
-func GetHostname() (string, error) {
- hostname, err := os.Hostname()
- if err != nil {
- return "User", err
- }
- return hostname, nil
-}
-
-// GetUsername returns the current user's username
-func GetUsername() (string, error) {
- currentUser, err := user.Current()
- if err != nil {
- return "User", err
- }
- return currentUser.Username, nil
-}
-
-func updateCfgFile(updateCfg func(config *Config)) error {
- if cfg == nil {
- return fmt.Errorf("config not loaded")
- }
-
- // Get the config file path
- configFile := viper.ConfigFileUsed()
- var configData []byte
- if configFile == "" {
- homeDir, err := os.UserHomeDir()
- if err != nil {
- return fmt.Errorf("failed to get home directory: %w", err)
- }
- configFile = filepath.Join(homeDir, fmt.Sprintf(".%s.json", appName))
- slog.Info("config file not found, creating new one", "path", configFile)
- configData = []byte(`{}`)
- } else {
- // Read the existing config file
- data, err := os.ReadFile(configFile)
- if err != nil {
- return fmt.Errorf("failed to read config file: %w", err)
- }
- configData = data
- }
-
- // Parse the JSON
- var userCfg *Config
- if err := json.Unmarshal(configData, &userCfg); err != nil {
- return fmt.Errorf("failed to parse config file: %w", err)
- }
-
- updateCfg(userCfg)
-
- // Write the updated config back to file
- updatedData, err := json.MarshalIndent(userCfg, "", " ")
- if err != nil {
- return fmt.Errorf("failed to marshal config: %w", err)
- }
-
- if err := os.WriteFile(configFile, updatedData, 0o644); err != nil {
- return fmt.Errorf("failed to write config file: %w", err)
- }
-
- return nil
-}
-
-// UpdateTheme updates the theme in the configuration and writes it to the config file.
-func UpdateTheme(themeName string) error {
- if cfg == nil {
- return fmt.Errorf("config not loaded")
- }
-
- // Update the in-memory config
- cfg.TUI.Theme = themeName
-
- // Update the file config
- return updateCfgFile(func(config *Config) {
- config.TUI.Theme = themeName
- })
-}
diff --git a/internal/config/init.go b/internal/config/init.go
deleted file mode 100644
index 5f8860f52..000000000
--- a/internal/config/init.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package config
-
-import (
- "fmt"
- "os"
- "path/filepath"
-)
-
-const (
- // InitFlagFilename is the name of the file that indicates whether the project has been initialized
- InitFlagFilename = "init"
-)
-
-// ProjectInitFlag represents the initialization status for a project directory
-type ProjectInitFlag struct {
- Initialized bool `json:"initialized"`
-}
-
-// ShouldShowInitDialog checks if the initialization dialog should be shown for the current directory
-func ShouldShowInitDialog() (bool, error) {
- if cfg == nil {
- return false, fmt.Errorf("config not loaded")
- }
-
- // Create the flag file path
- flagFilePath := filepath.Join(cfg.Data.Directory, InitFlagFilename)
-
- // Check if the flag file exists
- _, err := os.Stat(flagFilePath)
- if err == nil {
- // File exists, don't show the dialog
- return false, nil
- }
-
- // If the error is not "file not found", return the error
- if !os.IsNotExist(err) {
- return false, fmt.Errorf("failed to check init flag file: %w", err)
- }
-
- // File doesn't exist, show the dialog
- return true, nil
-}
-
-// MarkProjectInitialized marks the current project as initialized
-func MarkProjectInitialized() error {
- if cfg == nil {
- return fmt.Errorf("config not loaded")
- }
- // Create the flag file path
- flagFilePath := filepath.Join(cfg.Data.Directory, InitFlagFilename)
-
- // Create an empty file to mark the project as initialized
- file, err := os.Create(flagFilePath)
- if err != nil {
- return fmt.Errorf("failed to create init flag file: %w", err)
- }
- defer file.Close()
-
- return nil
-}