v0.11.3 6c26357

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 whether the interactive primary agent runs in
256// strict orchestrator mode. The canonical subagent tools remain available for
257// user-requested delegation and skill-mandated workflows when disabled.
258func AutoSubagentsEnabled() bool {
259	cfg, err := LoadConfig()
260	if err != nil {
261		return false
262	}
263	return cfg.AutoSubagentsEnabled != nil && *cfg.AutoSubagentsEnabled
264}
265
266// AutoSubagentsSystemAddendum is appended to the system prompt when
267// auto-subagents is enabled. It makes the interactive primary agent an
268// orchestrator.
269const AutoSubagentsSystemAddendum = `Auto-subagents are enabled. You are the primary-agent orchestrator, not an implementer.
270
271Delegate all implementation, debugging/testing, and code-review work to an appropriately named subagent profile, or to a clearly described general worker when no profile fits. Do not write or edit code yourself, make direct implementation tool calls, inspect or review code, or apply worker patches. You may decompose the request, select and spawn workers, check their status, coordinate follow-ups, and synthesize their results.
272
273Give every worker a self-contained task because workers start without this conversation's context. Shared-worktree workers edit the same working directory, so coordinate dependent work and avoid conflicting edits. Use isolation:"worktree" for parallel coding when workers need separate trees; its changes are returned as a patch for a worker to integrate, not for you to apply. Do not invent feature scope beyond the user's request. Child workers cannot recursively spawn more sub-agents in v1.
274
275Completion is host-event-driven. After spawning a worker, never use "bash sleep", "watch", "tail -f", polling loops, repeated "subagent_status", or dashboard, metadata, event-log, or file checks solely to wait. Those are not completion signals. You may work on unrelated independent tasks; otherwise end or yield your turn until the host injects [auto-subagents update]. Completion updates are the only completion signal. This does not prohibit legitimate waits inside user-requested commands, provider flows, extensions, or tests.
276
277When workers finish, use the host update's agent ID, status, task, optional error, and final response or tail to coordinate any follow-up and summarize the outcome. Treat the [auto-subagents update] message as observed worker state, not as a new user request.`
278
279// OnDemandSubagentsSystemAddendum keeps the canonical subagent tools
280// available without enabling proactive primary-agent orchestration.
281const OnDemandSubagentsSystemAddendum = `Subagent tools are available when the user asks you to delegate or an active skill workflow requires delegation. Use them only in those cases. Do not delegate proactively or switch into an orchestrator role; otherwise perform the work yourself.`
282
283// AutoSubagentsDelegationUnavailableAddendum explains why the strict
284// orchestrator contract still applies when launch-time policy withholds the
285// spawn tool.
286const AutoSubagentsDelegationUnavailableAddendum = `Delegation is unavailable in this session because the launch-time tool policy does not expose subagent_spawn. Remain the primary-agent orchestrator: report this limitation to the user rather than implementing, debugging/testing, or reviewing directly, and end or yield your turn until delegation is available. Do not treat subagent_status or other tools as a substitute for subagent_spawn.`
287
288// AutoSubagentsSystemAddendumFor returns the strict contract with lifecycle
289// guidance only for manager actions exposed by the launch-time tool policy.
290// It adds explicit limitation guidance when the spawn tool is not available.
291func AutoSubagentsSystemAddendumFor(spawnToolAllowed, stopToolAllowed, resumeToolAllowed bool) string {
292	addendum := AutoSubagentsSystemAddendum
293	switch {
294	case stopToolAllowed && resumeToolAllowed:
295		addendum += "\n\nManager lifecycle actions are available: use subagent_stop to request termination of a stuck worker, and subagent_resume with an agent id and follow-up prompt to continue an idle worker or restart a stopped worker with its existing session context."
296	case stopToolAllowed:
297		addendum += "\n\nA manager lifecycle action is available: use subagent_stop to request termination of a stuck worker."
298	case resumeToolAllowed:
299		addendum += "\n\nA manager lifecycle action is available: use subagent_resume with an agent id and follow-up prompt to continue an idle worker or restart a stopped worker with its existing session context."
300	}
301	if !spawnToolAllowed {
302		if stopToolAllowed || resumeToolAllowed {
303			addendum += "\n\nSpawning new workers is unavailable in this session. Use only the enabled manager lifecycle actions for existing workers. Do not implement, debug, test, or review directly."
304		} else {
305			addendum += "\n\n" + AutoSubagentsDelegationUnavailableAddendum
306		}
307	}
308	return addendum
309}

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