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) SetTUISubagentPosition(position string) error {
209 cfg, err := LoadConfig()
210 if err != nil {
211 return err
212 }
213 position = tui.NormalizeSubagentPosition(position)
214 if position == tui.SubagentPositionBelowInput {
215 cfg.TUISubagentPosition = ""
216 } else {
217 cfg.TUISubagentPosition = position
218 }
219 return SaveConfig(cfg)
220}
221
222func (configSettingsStore) SetReasoning(level string) error {
223 cfg, err := LoadConfig()
224 if err != nil {
225 return err
226 }
227 cfg.Reasoning = provider.NormalizeReasoning(level)
228 return SaveConfig(cfg)
229}
230
231func (configSettingsStore) SetTheme(name string) error {
232 cfg, err := LoadConfig()
233 if err != nil {
234 return err
235 }
236 if name == "auto" {
237 name = ""
238 }
239 cfg.Theme = name
240 return SaveConfig(cfg)
241}
242
243// AutoSubagentsEnabled reads the current auto-subagents flag from config.
244// The build/CLI startup path uses it when initially injecting
245// the auto-subagent tools into the tool registry; interactive mode mirrors
246// the setting into the live tool registry when its settings toggle changes.
247func AutoSubagentsEnabled() bool {
248 cfg, err := LoadConfig()
249 if err != nil {
250 return false
251 }
252 return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled
253}
254
255// AutoSubagentsSystemAddendum is appended to the system prompt when
256// auto-subagents is enabled, so the model knows it may delegate to
257// background sub-agents without the user having to mention the tool
258// by name. Kept short so it doesn't bloat the cached prompt prefix.
259const 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.
260
261Use 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.
262
263Do 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.
264
265When 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.`