// Package analyze turns fetched market data + news into an investment-advice // report via Claude, using one of two interchangeable modes: "subscription" // (shells out to the `claude` CLI, billed against a Claude Pro/Max // subscription) or "api" (calls the Messages API directly, billed per token). package analyze import ( "context" "fmt" "github.com/ytshih/stonks/internal/config" ) // Analyzer sends a prompt to Claude and returns the response text. type Analyzer interface { Analyze(ctx context.Context, prompt string) (string, error) } // Constructor builds an Analyzer from the analyzer config block. type Constructor func(cfg config.AnalyzerConfig) (Analyzer, error) var registry = map[string]Constructor{} // Register makes an analyzer constructor available under mode. Called from // each mode implementation's init(). func Register(mode string, ctor Constructor) { if _, exists := registry[mode]; exists { panic(fmt.Sprintf("analyze: constructor already registered for mode %q", mode)) } registry[mode] = ctor } // Build looks up the constructor for cfg.Mode and invokes it. func Build(cfg config.AnalyzerConfig) (Analyzer, error) { ctor, ok := registry[cfg.Mode] if !ok { return nil, fmt.Errorf("analyze: no analyzer registered for mode %q", cfg.Mode) } return ctor(cfg) }