feat: AI suggestion (#34)

## Feat
* feat(ai): add AI suggestion engine, context gathering, and ghost text
overlay (`v0.3.0`)

## Security & Perf
* fix(ai): restrict background `--help` execution to a hardcoded command
allowlist to prevent RCE
* fix(root): defer context cancellation in goroutine to prevent resource
leaks
* perf(ai): implement LRU eviction and maximum size limit for
`ProviderCache`
* perf(ai): truncate command context output
(`CommandContextProvider.Gather`) to 1000 characters to save tokens

## Bug Fixes
* fix(ai): add mutex synchronization and thread-safe snapshotting to
`AIEngine.RegisterProvider` and `GatherDynamicContext`
* fix(ai): return a copy of fresh `Suggestion` in `AIEngine.Suggest` to
prevent cache mutation
* fix(ai): use length-prefixed encoding in `EnvSnapshot.Hash` to prevent
delimiter collisions
* fix(ai): skip variable assignment lines containing `=` when extracting
Makefile targets
* fix(runner): check `scanner.Err()` and return `nil` on scan errors in
justfile generator
* test(ci): rename `git commit` to `git checkout` in overlay test to
resolve CI `typos` false positive
This commit is contained in:
VERSE
2026-07-11 15:17:14 +07:00
committed by GitHub
parent 4728ca0c7f
commit 94c8af7b1d
29 changed files with 2263 additions and 45 deletions
+44
View File
@@ -57,11 +57,55 @@ type UpdaterConfig struct {
CheckInterval Duration `toml:"check-interval"`
}
type SuggestOnEmptyConfig struct {
Enabled bool `toml:"enabled"`
DebounceMS int `toml:"debounce_ms"`
MinIntervalMS int `toml:"min_interval_ms"`
}
type ProviderConfig struct {
InheritedFrom string `toml:"inherited_from"`
Endpoint string `toml:"endpoint"`
APIKey string `toml:"api_key"`
APIKeyEnv string `toml:"api_key_env"`
Model string `toml:"model"`
TimeoutMS int `toml:"timeout_ms"`
ExtraRequestBody map[string]any `toml:"extra_request_body"`
}
type AIConfig struct {
Enabled bool `toml:"enabled"`
Provider string `toml:"provider"`
DebounceMS int `toml:"debounce_ms"`
MinIntervalMS int `toml:"min_interval_ms"`
Providers map[string]ProviderConfig `toml:"providers"`
SuggestOnEmpty SuggestOnEmptyConfig `toml:"suggest_on_empty"`
}
func (c *AIConfig) GetActiveProvider() (ProviderConfig, bool) {
if c.Providers == nil {
return ProviderConfig{}, false
}
p, ok := c.Providers[c.Provider]
return p, ok
}
func (p *ProviderConfig) GetAPIKey() string {
if p.APIKey != "" {
return p.APIKey
}
if p.APIKeyEnv != "" {
return os.Getenv(p.APIKeyEnv)
}
return ""
}
type Config struct {
Core CoreConfig `toml:"core"`
UI UIConfig `toml:"ui"`
Git GitConfig `toml:"git"`
Updater UpdaterConfig `toml:"updater"`
AI AIConfig `toml:"ai"`
}
var (