v0.0.42 ac6d556

args.go RawHistoryFolder

1package agent
2
3import (
4	"fmt"
5	"os"
6	"strings"
7)
8
9// Mode is the CLI run mode.
10type Mode string
11
12const (
13	ModeInteractive Mode = "interactive"
14	ModePrint       Mode = "print"
15	ModeJSON        Mode = "json"
16	ModeRPC         Mode = "rpc"
17)
18
19// Args holds parsed command-line options.
20type Args struct {
21	Mode     Mode
22	Provider string
23	Model    string
24	APIKey   string
25
26	BaseURL            string // override provider base URL (for tests/self-hosted)
27	SystemPrompt       string
28	AppendSystemPrompt []string
29	Reasoning          string
30
31	Continue bool
32	Resume   bool
33	Session  string
34	NoSess   bool
35
36	CWD      string
37	NoTools  bool
38	Tools    []string
39	MaxSteps int
40
41	// Exts is a list of directory paths the user passed via --ext.
42	// Each must contain an extension.json. Loaded for one session
43	// only; never persisted. Take precedence over installed exts of
44	// the same name.
45	Exts []string
46
47	// NoExt disables extension discovery + spawn entirely for this
48	// run. --ext PATH still works (explicit beats implicit) so you
49	// can run "with only this one extension" via --no-ext --ext PATH.
50	NoExt bool
51
52	// NoSkill disables ALL skill discovery for this run, including
53	// the built-in skills compiled into the binary. The system
54	// prompt loses its "Available skills" manifest and the `skill`
55	// tool isn't registered. Useful for running zot without any
56	// extra context biasing the model.
57	NoSkill bool
58
59	// WithSkills opts into loading user-installed skills from
60	// $ZOT_HOME/skills/, .zot/skills/, .claude/skills/, and
61	// .agents/skills/. Without this flag only the built-in skills
62	// shipped with the zot binary are available, so a fresh install
63	// has a deterministic skill set regardless of what's lying
64	// around in the user's home directory.
65	WithSkills bool
66
67	// NoYolo turns on per-tool confirmation. Before each tool
68	// invocation the TUI prompts the user with the tool name + args
69	// and waits for an explicit yes/no. The user can also pick
70	// "always for this tool this session" or "always for anything
71	// this session" to stop being prompted again. Defaults off
72	// (yolo mode): tools run without asking. In -p / --json / rpc
73	// modes there's no interactive TUI, so --no-yolo auto-refuses
74	// every tool call with a reason the model can learn from.
75	NoYolo bool
76
77	ListModels bool
78	Help       bool
79	Version    bool
80
81	Prompt string // concatenated positional args
82}
83
84// ParseArgs parses the process arguments (excluding argv[0]).
85func ParseArgs(in []string) (Args, error) {
86	a := Args{Mode: ModeInteractive, MaxSteps: 50}
87	positional := []string{}
88
89	want := func(i *int, flag string) (string, error) {
90		*i++
91		if *i >= len(in) {
92			return "", fmt.Errorf("%s requires a value", flag)
93		}
94		return in[*i], nil
95	}
96
97	for i := 0; i < len(in); i++ {
98		arg := in[i]
99		switch arg {
100		case "-h", "--help":
101			a.Help = true
102		case "-v", "--version":
103			a.Version = true
104		case "-p", "--print":
105			a.Mode = ModePrint
106		case "--json":
107			a.Mode = ModeJSON
108		case "--rpc":
109			a.Mode = ModeRPC
110		case "-c", "--continue":
111			a.Continue = true
112		case "-r", "--resume":
113			a.Resume = true
114		case "--no-session":
115			a.NoSess = true
116		case "--no-tools":
117			a.NoTools = true
118		case "--list-models":
119			a.ListModels = true
120		case "--experimental-oauth":
121			// deprecated: subscription login is always available.
122			// accepted silently for backwards compatibility.
123		case "--provider":
124			v, err := want(&i, arg)
125			if err != nil {
126				return a, err
127			}
128			a.Provider = v
129		case "--model":
130			v, err := want(&i, arg)
131			if err != nil {
132				return a, err
133			}
134			a.Model = v
135		case "--api-key":
136			v, err := want(&i, arg)
137			if err != nil {
138				return a, err
139			}
140			a.APIKey = v
141		case "--base-url":
142			v, err := want(&i, arg)
143			if err != nil {
144				return a, err
145			}
146			a.BaseURL = v
147		case "--system-prompt":
148			v, err := want(&i, arg)
149			if err != nil {
150				return a, err
151			}
152			a.SystemPrompt = v
153		case "--append-system-prompt":
154			v, err := want(&i, arg)
155			if err != nil {
156				return a, err
157			}
158			a.AppendSystemPrompt = append(a.AppendSystemPrompt, v)
159		case "--ext", "-e":
160			v, err := want(&i, arg)
161			if err != nil {
162				return a, err
163			}
164			// Repeatable; each value is a directory containing an
165			// extension.json. Resolved to absolute later so paths like
166			// "." survive a later cwd change.
167			a.Exts = append(a.Exts, v)
168		case "--no-ext", "--no-extensions":
169			a.NoExt = true
170		case "--no-skill", "--no-skills":
171			a.NoSkill = true
172		case "--with-skills", "--with-skill":
173			a.WithSkills = true
174		case "--no-yolo":
175			a.NoYolo = true
176		case "--reasoning":
177			v, err := want(&i, arg)
178			if err != nil {
179				return a, err
180			}
181			switch strings.ToLower(v) {
182			case "", "low", "medium", "high":
183				a.Reasoning = strings.ToLower(v)
184			default:
185				return a, fmt.Errorf("--reasoning must be low|medium|high")
186			}
187		case "--session":
188			v, err := want(&i, arg)
189			if err != nil {
190				return a, err
191			}
192			a.Session = v
193		case "--cwd":
194			v, err := want(&i, arg)
195			if err != nil {
196				return a, err
197			}
198			a.CWD = v
199		case "--tools":
200			v, err := want(&i, arg)
201			if err != nil {
202				return a, err
203			}
204			for _, t := range strings.Split(v, ",") {
205				t = strings.TrimSpace(t)
206				if t != "" {
207					a.Tools = append(a.Tools, t)
208				}
209			}
210		case "--max-steps":
211			v, err := want(&i, arg)
212			if err != nil {
213				return a, err
214			}
215			var n int
216			if _, err := fmt.Sscanf(v, "%d", &n); err != nil || n <= 0 {
217				return a, fmt.Errorf("--max-steps must be a positive integer")
218			}
219			a.MaxSteps = n
220		default:
221			if strings.HasPrefix(arg, "-") && arg != "-" {
222				return a, fmt.Errorf("unknown flag %q", arg)
223			}
224			positional = append(positional, arg)
225		}
226	}
227
228	if len(positional) > 0 {
229		a.Prompt = strings.Join(positional, " ")
230	}
231
232	if a.CWD == "" {
233		a.CWD, _ = os.Getwd()
234	}
235	return a, nil
236}
237
238// PrintHelp writes the help text to w (stderr by default).
239func PrintHelp(version string) {
240	fmt.Fprintf(os.Stderr, `zot %s — Yet another coding agent harness.
241
242usage:
243  zot                          interactive tui
244  zot "prompt"                 interactive, pre-filled prompt
245  zot -p "prompt"              print final text, exit
246  zot --json "prompt"          newline-delimited json events, exit
247  zot rpc                      json-rpc loop on stdin/stdout (see docs/rpc.md)
248  zot telegram-bot setup       configure a telegram bot (from BotFather)
249  zot telegram-bot run         foreground bridge (ctrl+c to stop)
250  zot telegram-bot start       background bridge (detached)
251  zot telegram-bot stop        stop the background bridge
252  zot telegram-bot logs [-f]   tail the background bridge's log
253  zot telegram-bot status      config + running state
254  zot telegram-bot reset       forget saved token
255  (short alias: zot tg ...)
256
257flags:
258  --provider  anthropic|openai
259  --model     model id (see --list-models)
260  --api-key   api key for this run (falls back to env and auth.json)
261  --base-url  override provider api base url (for testing/self-hosted)
262
263  --system-prompt TEXT         replace the default system prompt
264  --append-system-prompt TEXT  append to the system prompt (repeatable)
265  --reasoning low|medium|high  enable reasoning on supported models
266
267  -c, --continue               continue the most recent session for this cwd
268  -r, --resume                 pick a session to resume
269  --session PATH               resume a specific session file
270  --no-session                 do not read or write a session file
271
272  --cwd PATH                   treat PATH as the working directory
273  --no-tools                   disable all tools
274  --tools csv                  only enable the listed tools
275  -e, --ext PATH               load an extension from PATH for this run
276                               (repeatable; takes precedence over
277                               installed extensions of the same name)
278  --no-ext                     skip extension discovery for this run
279                               (--ext PATH still works on top, so
280                               --no-ext --ext ./x runs only x)
281  --no-skill                   skip skill discovery for this run,
282                               including built-in skills (no skill
283                               tool, no Available skills manifest)
284  --with-skills                load user-installed skills from
285                               $ZOT_HOME/skills/ + .zot/skills/ +
286                               .claude/skills/ + .agents/skills/.
287                               default: only built-in skills load
288
289  --no-yolo                    ask before running every tool call
290                               (interactive mode only; in -p / --json
291                               / rpc this refuses every tool call)
292
293  --max-steps N                agent loop iteration cap (default 50)
294  --list-models                print known models and exit
295  -h, --help                   this message
296  -v, --version                version info
297`, version)
298}

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