v0.4.0 a622146

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) 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.`

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