151 lines
3.7 KiB
Go
151 lines
3.7 KiB
Go
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/hjbdev/patterm/internal/preset"
|
|
)
|
|
|
|
const (
|
|
defaultSummaryProvider = "codex"
|
|
defaultCodexModel = "gpt-5.4-mini"
|
|
defaultOpenCodeModel = "opencode-go/minimax-m2.7"
|
|
defaultClaudeModel = "claude-haiku-4-5"
|
|
)
|
|
|
|
type settings struct {
|
|
AutoSummary autoSummarySettings `json:"auto_summary"`
|
|
}
|
|
|
|
type autoSummarySettings struct {
|
|
Enabled bool `json:"enabled"`
|
|
Provider string `json:"provider"`
|
|
Models map[string]string `json:"models"`
|
|
Cadence string `json:"cadence"`
|
|
QuietWindowMS int `json:"quiet_window_ms"`
|
|
MinInputChars int `json:"min_input_chars"`
|
|
MaxHistoryChars int `json:"max_history_chars"`
|
|
}
|
|
|
|
func defaultSettings() settings {
|
|
return settings{
|
|
AutoSummary: autoSummarySettings{
|
|
Enabled: true,
|
|
Provider: defaultSummaryProvider,
|
|
Models: defaultSummaryModels(),
|
|
Cadence: "1m",
|
|
QuietWindowMS: 3000,
|
|
MinInputChars: 4,
|
|
MaxHistoryChars: 12000,
|
|
},
|
|
}
|
|
}
|
|
|
|
func defaultSummaryModels() map[string]string {
|
|
return map[string]string{
|
|
"codex": defaultCodexModel,
|
|
"opencode": defaultOpenCodeModel,
|
|
"claude": defaultClaudeModel,
|
|
}
|
|
}
|
|
|
|
func loadSettings() (settings, string, error) {
|
|
base, err := preset.ConfigDir()
|
|
if err != nil {
|
|
return settings{}, "", err
|
|
}
|
|
path := filepath.Join(base, "settings.json")
|
|
st := defaultSettings()
|
|
b, err := os.ReadFile(path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return st, path, nil
|
|
}
|
|
return st, path, fmt.Errorf("settings: read %s: %w", path, err)
|
|
}
|
|
if err := json.Unmarshal(b, &st); err != nil {
|
|
return defaultSettings(), path, fmt.Errorf("settings: parse %s: %w", path, err)
|
|
}
|
|
st.normalize()
|
|
return st, path, nil
|
|
}
|
|
|
|
func saveSettings(path string, st settings) error {
|
|
if path == "" {
|
|
return fmt.Errorf("settings: empty path")
|
|
}
|
|
st.normalize()
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {
|
|
return err
|
|
}
|
|
b, err := json.MarshalIndent(st, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
b = append(b, '\n')
|
|
return os.WriteFile(path, b, 0o600)
|
|
}
|
|
|
|
func (st *settings) normalize() {
|
|
def := defaultSettings()
|
|
if st.AutoSummary.Provider == "" {
|
|
st.AutoSummary.Provider = def.AutoSummary.Provider
|
|
}
|
|
switch st.AutoSummary.Provider {
|
|
case "codex", "opencode", "claude":
|
|
default:
|
|
st.AutoSummary.Provider = def.AutoSummary.Provider
|
|
}
|
|
if st.AutoSummary.Models == nil {
|
|
st.AutoSummary.Models = defaultSummaryModels()
|
|
} else {
|
|
for k, v := range defaultSummaryModels() {
|
|
if st.AutoSummary.Models[k] == "" {
|
|
st.AutoSummary.Models[k] = v
|
|
}
|
|
}
|
|
}
|
|
if st.AutoSummary.Cadence == "" {
|
|
st.AutoSummary.Cadence = def.AutoSummary.Cadence
|
|
}
|
|
if st.AutoSummary.QuietWindowMS <= 0 {
|
|
st.AutoSummary.QuietWindowMS = def.AutoSummary.QuietWindowMS
|
|
}
|
|
if st.AutoSummary.MinInputChars <= 0 {
|
|
st.AutoSummary.MinInputChars = def.AutoSummary.MinInputChars
|
|
}
|
|
if st.AutoSummary.MaxHistoryChars <= 0 {
|
|
st.AutoSummary.MaxHistoryChars = def.AutoSummary.MaxHistoryChars
|
|
}
|
|
}
|
|
|
|
func (st settings) clone() settings {
|
|
st.normalize()
|
|
if st.AutoSummary.Models != nil {
|
|
models := make(map[string]string, len(st.AutoSummary.Models))
|
|
for k, v := range st.AutoSummary.Models {
|
|
models[k] = v
|
|
}
|
|
st.AutoSummary.Models = models
|
|
}
|
|
return st
|
|
}
|
|
|
|
func (a autoSummarySettings) clone() autoSummarySettings {
|
|
st := settings{AutoSummary: a}.clone()
|
|
return st.AutoSummary
|
|
}
|
|
|
|
func (a autoSummarySettings) modelFor(provider string) string {
|
|
if a.Models == nil {
|
|
return defaultSummaryModels()[provider]
|
|
}
|
|
if m := a.Models[provider]; m != "" {
|
|
return m
|
|
}
|
|
return defaultSummaryModels()[provider]
|
|
}
|