v0.9.1 406ddf4

settings_store.go RawHistoryFolder

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) SetWebSearchEnabled(enabled bool) error {
86	cfg, err := LoadConfig()
87	if err != nil {
88		return fmt.Errorf("load config for web search setting: %w", err)
89	}
90	cfg.WebSearchEnabled = &enabled
91	if err := SaveConfig(cfg); err != nil {
92		return fmt.Errorf("save web search setting: %w", err)
93	}
94	return nil
95}
96
97func (configSettingsStore) SetFastMode(enabled bool) error {
98	cfg, err := LoadConfig()
99	if err != nil {
100		return err
101	}
102	cfg.FastMode = &enabled
103	return SaveConfig(cfg)
104}
105
106func (configSettingsStore) SetLSPEnabled(enabled bool) error {
107	cfg, err := LoadConfig()
108	if err != nil {
109		return err
110	}
111	cfg.LSPEnabled = &enabled
112	return SaveConfig(cfg)
113}
114
115func (configSettingsStore) SetSubagentLSPEnabled(enabled bool) error {
116	cfg, err := LoadConfig()
117	if err != nil {
118		return err
119	}
120	cfg.SubagentLSPEnabled = &enabled
121	return SaveConfig(cfg)
122}
123
124func (configSettingsStore) SetAutoCompactThreshold(percent int) error {
125	cfg, err := LoadConfig()
126	if err != nil {
127		return err
128	}
129	cfg.AutoCompactThreshold = &percent
130	return SaveConfig(cfg)
131}
132
133func (configSettingsStore) SetJailByDefault(enabled bool) error {
134	cfg, err := LoadConfig()
135	if err != nil {
136		return err
137	}
138	cfg.JailByDefault = &enabled
139	return SaveConfig(cfg)
140}
141
142func (configSettingsStore) SetRecursiveFileSuggest(enabled bool) error {
143	cfg, err := LoadConfig()
144	if err != nil {
145		return err
146	}
147	cfg.RecursiveFileSuggest = &enabled
148	return SaveConfig(cfg)
149}
150
151func (configSettingsStore) SetRespectGitignore(enabled bool) error {
152	cfg, err := LoadConfig()
153	if err != nil {
154		return err
155	}
156	cfg.RespectGitignore = &enabled
157	return SaveConfig(cfg)
158}
159
160func (configSettingsStore) SetCompactMode(enabled bool) error {
161	cfg, err := LoadConfig()
162	if err != nil {
163		return err
164	}
165	cfg.CompactMode = &enabled
166	return SaveConfig(cfg)
167}
168
169func (configSettingsStore) SetShowInstructionsAtStartup(enabled bool) error {
170	cfg, err := LoadConfig()
171	if err != nil {
172		return err
173	}
174	cfg.ShowInstructionsAtStartup = &enabled
175	return SaveConfig(cfg)
176}
177
178func (configSettingsStore) SetTUIInputStyle(style string) error {
179	cfg, err := LoadConfig()
180	if err != nil {
181		return err
182	}
183	style = tui.NormalizeInputStyle(style)
184	if style == tui.InputStylePlain {
185		cfg.TUIInputStyle = ""
186	} else {
187		cfg.TUIInputStyle = style
188	}
189	return SaveConfig(cfg)
190}
191
192func (configSettingsStore) SetTUIStatusPosition(position string) error {
193	cfg, err := LoadConfig()
194	if err != nil {
195		return err
196	}
197	position = tui.NormalizeStatusPosition(position)
198	if position == tui.StatusPositionAboveInput {
199		cfg.TUIStatusPosition = ""
200	} else {
201		cfg.TUIStatusPosition = position
202	}
203	return SaveConfig(cfg)
204}
205
206func (configSettingsStore) SetTUIWorkingPosition(position string) error {
207	cfg, err := LoadConfig()
208	if err != nil {
209		return err
210	}
211	position = tui.NormalizeWorkingPosition(position)
212	if position == tui.WorkingPositionAboveInput {
213		cfg.TUIWorkingPosition = ""
214	} else {
215		cfg.TUIWorkingPosition = position
216	}
217	return SaveConfig(cfg)
218}
219
220func (configSettingsStore) SetTUISubagentPosition(position string) error {
221	cfg, err := LoadConfig()
222	if err != nil {
223		return err
224	}
225	position = tui.NormalizeSubagentPosition(position)
226	if position == tui.SubagentPositionBelowInput {
227		cfg.TUISubagentPosition = ""
228	} else {
229		cfg.TUISubagentPosition = position
230	}
231	return SaveConfig(cfg)
232}
233
234func (configSettingsStore) SetReasoning(level string) error {
235	cfg, err := LoadConfig()
236	if err != nil {
237		return err
238	}
239	cfg.Reasoning = provider.NormalizeReasoning(level)
240	return SaveConfig(cfg)
241}
242
243func (configSettingsStore) SetTheme(name string) error {
244	cfg, err := LoadConfig()
245	if err != nil {
246		return err
247	}
248	if name == "auto" {
249		name = ""
250	}
251	cfg.Theme = name
252	return SaveConfig(cfg)
253}
254
255// AutoSubagentsEnabled reads the current auto-subagents flag from config.
256// The build/CLI startup path uses it when initially injecting
257// the auto-subagent tools into the tool registry; interactive mode mirrors
258// the setting into the live tool registry when its settings toggle changes.
259func AutoSubagentsEnabled() bool {
260	cfg, err := LoadConfig()
261	if err != nil {
262		return false
263	}
264	return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled
265}
266
267// AutoSubagentsSystemAddendum is appended to the system prompt when
268// auto-subagents is enabled, so the model knows it may delegate to
269// background sub-agents without the user having to mention the tool
270// by name. Kept short so it doesn't bloat the cached prompt prefix.
271const 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.
272
273Use 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.
274
275Do 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.
276
277When 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.`

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close