1package agent
2
3import (
4 "fmt"
5
6 "github.com/bnema/zut/packages/provider"
7 "github.com/bnema/zut/packages/tui"
8)
9
10type configSettingsStore struct{}
11
12func (configSettingsStore) SetQuickModelShortcut(slot int, providerName, model string) error {
13 if slot < 1 || slot > 9 {
14 return nil
15 }
16 cfg, err := LoadConfig()
17 if err != nil {
18 return err
19 }
20 if len(cfg.QuickModelShortcuts) < slot {
21 next := make([]QuickModelShortcut, slot)
22 copy(next, cfg.QuickModelShortcuts)
23 cfg.QuickModelShortcuts = next
24 }
25 cfg.QuickModelShortcuts[slot-1] = QuickModelShortcut{Provider: providerName, Model: model}
26 // Trim trailing empty slots so config.json stays compact.
27 for len(cfg.QuickModelShortcuts) > 0 {
28 last := cfg.QuickModelShortcuts[len(cfg.QuickModelShortcuts)-1]
29 if last.Provider != "" || last.Model != "" {
30 break
31 }
32 cfg.QuickModelShortcuts = cfg.QuickModelShortcuts[:len(cfg.QuickModelShortcuts)-1]
33 }
34 return SaveConfig(cfg)
35}
36
37func (configSettingsStore) SetInlineImages(enabled bool) error {
38 cfg, err := LoadConfig()
39 if err != nil {
40 return err
41 }
42 cfg.InlineImagesEnabled = &enabled
43 return SaveConfig(cfg)
44}
45
46func (configSettingsStore) SetTerminalAlertsEnabled(enabled bool) error {
47 cfg, err := LoadConfig()
48 if err != nil {
49 return err
50 }
51 cfg.TerminalAlertsEnabled = &enabled
52 return SaveConfig(cfg)
53}
54
55func (configSettingsStore) SetTerminalTitleEnabled(enabled bool) error {
56 cfg, err := LoadConfig()
57 if err != nil {
58 return err
59 }
60 cfg.TerminalTitleEnabled = &enabled
61 return SaveConfig(cfg)
62}
63
64func (configSettingsStore) SetAutoSubagents(enabled bool) error {
65 cfg, err := LoadConfig()
66 if err != nil {
67 return err
68 }
69 cfg.AutoSubagentsEnabled = &enabled
70 return SaveConfig(cfg)
71}
72
73func (configSettingsStore) SetPonytailEnabled(enabled bool) error {
74 cfg, err := LoadConfig()
75 if err != nil {
76 return fmt.Errorf("load config for Ponytail setting: %w", err)
77 }
78 cfg.PonytailEnabled = &enabled
79 if err := SaveConfig(cfg); err != nil {
80 return fmt.Errorf("save Ponytail setting: %w", err)
81 }
82 return nil
83}
84
85func (configSettingsStore) SetFastMode(enabled bool) error {
86 cfg, err := LoadConfig()
87 if err != nil {
88 return err
89 }
90 cfg.FastMode = &enabled
91 return SaveConfig(cfg)
92}
93
94func (configSettingsStore) SetLSPEnabled(enabled bool) error {
95 cfg, err := LoadConfig()
96 if err != nil {
97 return err
98 }
99 cfg.LSPEnabled = &enabled
100 return SaveConfig(cfg)
101}
102
103func (configSettingsStore) SetSubagentLSPEnabled(enabled bool) error {
104 cfg, err := LoadConfig()
105 if err != nil {
106 return err
107 }
108 cfg.SubagentLSPEnabled = &enabled
109 return SaveConfig(cfg)
110}
111
112func (configSettingsStore) SetAutoCompactThreshold(percent int) error {
113 cfg, err := LoadConfig()
114 if err != nil {
115 return err
116 }
117 cfg.AutoCompactThreshold = &percent
118 return SaveConfig(cfg)
119}
120
121func (configSettingsStore) SetJailByDefault(enabled bool) error {
122 cfg, err := LoadConfig()
123 if err != nil {
124 return err
125 }
126 cfg.JailByDefault = &enabled
127 return SaveConfig(cfg)
128}
129
130func (configSettingsStore) SetRecursiveFileSuggest(enabled bool) error {
131 cfg, err := LoadConfig()
132 if err != nil {
133 return err
134 }
135 cfg.RecursiveFileSuggest = &enabled
136 return SaveConfig(cfg)
137}
138
139func (configSettingsStore) SetRespectGitignore(enabled bool) error {
140 cfg, err := LoadConfig()
141 if err != nil {
142 return err
143 }
144 cfg.RespectGitignore = &enabled
145 return SaveConfig(cfg)
146}
147
148func (configSettingsStore) SetCompactMode(enabled bool) error {
149 cfg, err := LoadConfig()
150 if err != nil {
151 return err
152 }
153 cfg.CompactMode = &enabled
154 return SaveConfig(cfg)
155}
156
157func (configSettingsStore) SetShowInstructionsAtStartup(enabled bool) error {
158 cfg, err := LoadConfig()
159 if err != nil {
160 return err
161 }
162 cfg.ShowInstructionsAtStartup = &enabled
163 return SaveConfig(cfg)
164}
165
166func (configSettingsStore) SetTUIInputStyle(style string) error {
167 cfg, err := LoadConfig()
168 if err != nil {
169 return err
170 }
171 style = tui.NormalizeInputStyle(style)
172 if style == tui.InputStylePlain {
173 cfg.TUIInputStyle = ""
174 } else {
175 cfg.TUIInputStyle = style
176 }
177 return SaveConfig(cfg)
178}
179
180func (configSettingsStore) SetTUIStatusPosition(position string) error {
181 cfg, err := LoadConfig()
182 if err != nil {
183 return err
184 }
185 position = tui.NormalizeStatusPosition(position)
186 if position == tui.StatusPositionAboveInput {
187 cfg.TUIStatusPosition = ""
188 } else {
189 cfg.TUIStatusPosition = position
190 }
191 return SaveConfig(cfg)
192}
193
194func (configSettingsStore) SetTUIWorkingPosition(position string) error {
195 cfg, err := LoadConfig()
196 if err != nil {
197 return err
198 }
199 position = tui.NormalizeWorkingPosition(position)
200 if position == tui.WorkingPositionAboveInput {
201 cfg.TUIWorkingPosition = ""
202 } else {
203 cfg.TUIWorkingPosition = position
204 }
205 return SaveConfig(cfg)
206}
207
208func (configSettingsStore) SetReasoning(level string) error {
209 cfg, err := LoadConfig()
210 if err != nil {
211 return err
212 }
213 cfg.Reasoning = provider.NormalizeReasoning(level)
214 return SaveConfig(cfg)
215}
216
217func (configSettingsStore) SetTheme(name string) error {
218 cfg, err := LoadConfig()
219 if err != nil {
220 return err
221 }
222 if name == "auto" {
223 name = ""
224 }
225 cfg.Theme = name
226 return SaveConfig(cfg)
227}
228
229// AutoSubagentsEnabled reads the current auto-subagents flag from config.
230// The build/CLI startup path uses it when initially injecting
231// the auto-subagent tools into the tool registry; interactive mode mirrors
232// the setting into the live tool registry when its settings toggle changes.
233func AutoSubagentsEnabled() bool {
234 cfg, err := LoadConfig()
235 if err != nil {
236 return false
237 }
238 return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled
239}
240
241// AutoSubagentsSystemAddendum is appended to the system prompt when
242// auto-subagents is enabled, so the model knows it may delegate to
243// background sub-agents without the user having to mention the tool
244// by name. Kept short so it doesn't bloat the cached prompt prefix.
245const AutoSubagentsSystemAddendum = `Auto-subagents are enabled. When available, subagent_spawn starts background sub-agents in separate long-lived processes, and subagent_status reports their live state without waiting for completion.
246
247Use it proactively when the user's request naturally splits into independent sub-tasks that can run concurrently; prefer isolation:"worktree" for parallel coding (e.g. "refactor module A and module B", "write the implementation and the tests", "investigate three separate files"). Spawn one sub-agent per independent sub-task with a self-contained task description (sub-agents start with no context from this conversation). If [subagents_list] is present, choose the named profile whose description best matches the task and pass its name as the tool's agent field. Continue working on the remaining or coordinating work yourself in parallel; do not wait for sub-agents to finish before responding. Briefly tell the user which sub-agents you spawned and what each is doing. When workers finish, use the host update's agent ID, status, task, optional error, and final response or tail to summarize the outcome and decide any follow-up.
248
249Do NOT use subagent_spawn for trivial single-step work, for tasks that depend on each other sequentially, or when the user explicitly asked you to do the work yourself. Child workers cannot recursively spawn more sub-agents in v1.
250
251When every sub-agent you spawned reaches a terminal state, the host injects a single [auto-subagents update] message containing each agent's id, status, task, optional error, and final response (or a tail when the response is long). Treat that message as observed state (not as a new user request) and write a short follow-up summary referencing each agent by id.`