v0.0.88 2cf21d7

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.
73	//
74	// No effect in -p / --json / rpc modes, which have no
75	// interactive prompt. A warning is printed to stderr on startup
76	// so scripts know the flag is ignored, but tools still run
77	// freely so automated workflows keep working.
78	NoYolo bool
79
80	ListModels bool
81	Help       bool
82	Version    bool
83
84	Prompt string // concatenated positional args
85}
86
87// ParseArgs parses the process arguments (excluding argv[0]).
88func ParseArgs(in []string) (Args, error) {
89	a := Args{Mode: ModeInteractive, MaxSteps: 50}
90	positional := []string{}
91
92	want := func(i *int, flag string) (string, error) {
93		*i++
94		if *i >= len(in) {
95			return "", fmt.Errorf("%s requires a value", flag)
96		}
97		return in[*i], nil
98	}
99
100	for i := 0; i < len(in); i++ {
101		arg := in[i]
102		switch arg {
103		case "-h", "--help":
104			a.Help = true
105		case "-v", "--version":
106			a.Version = true
107		case "-p", "--print":
108			a.Mode = ModePrint
109		case "--json":
110			a.Mode = ModeJSON
111		case "--rpc":
112			a.Mode = ModeRPC
113		case "-c", "--continue":
114			a.Continue = true
115		case "-r", "--resume":
116			a.Resume = true
117		case "--no-session":
118			a.NoSess = true
119		case "--no-tools":
120			a.NoTools = true
121		case "--list-models":
122			a.ListModels = true
123		case "--experimental-oauth":
124			// deprecated: subscription login is always available.
125			// accepted silently for backwards compatibility.
126		case "--provider":
127			v, err := want(&i, arg)
128			if err != nil {
129				return a, err
130			}
131			a.Provider = v
132		case "--model":
133			v, err := want(&i, arg)
134			if err != nil {
135				return a, err
136			}
137			a.Model = v
138		case "--api-key":
139			v, err := want(&i, arg)
140			if err != nil {
141				return a, err
142			}
143			a.APIKey = v
144		case "--base-url":
145			v, err := want(&i, arg)
146			if err != nil {
147				return a, err
148			}
149			a.BaseURL = v
150		case "--system-prompt":
151			v, err := want(&i, arg)
152			if err != nil {
153				return a, err
154			}
155			a.SystemPrompt = v
156		case "--append-system-prompt":
157			v, err := want(&i, arg)
158			if err != nil {
159				return a, err
160			}
161			a.AppendSystemPrompt = append(a.AppendSystemPrompt, v)
162		case "--ext", "-e":
163			v, err := want(&i, arg)
164			if err != nil {
165				return a, err
166			}
167			// Repeatable; each value is a directory containing an
168			// extension.json. Resolved to absolute later so paths like
169			// "." survive a later cwd change.
170			a.Exts = append(a.Exts, v)
171		case "--no-ext", "--no-extensions":
172			a.NoExt = true
173		case "--no-skill", "--no-skills":
174			a.NoSkill = true
175		case "--with-skills", "--with-skill":
176			a.WithSkills = true
177		case "--no-yolo":
178			a.NoYolo = true
179		case "--reasoning":
180			v, err := want(&i, arg)
181			if err != nil {
182				return a, err
183			}
184			switch strings.ToLower(v) {
185			case "", "low", "medium", "high":
186				a.Reasoning = strings.ToLower(v)
187			default:
188				return a, fmt.Errorf("--reasoning must be low|medium|high")
189			}
190		case "--session":
191			v, err := want(&i, arg)
192			if err != nil {
193				return a, err
194			}
195			a.Session = v
196		case "--cwd":
197			v, err := want(&i, arg)
198			if err != nil {
199				return a, err
200			}
201			a.CWD = v
202		case "--tools":
203			v, err := want(&i, arg)
204			if err != nil {
205				return a, err
206			}
207			for _, t := range strings.Split(v, ",") {
208				t = strings.TrimSpace(t)
209				if t != "" {
210					a.Tools = append(a.Tools, t)
211				}
212			}
213		case "--max-steps":
214			v, err := want(&i, arg)
215			if err != nil {
216				return a, err
217			}
218			var n int
219			if _, err := fmt.Sscanf(v, "%d", &n); err != nil || n <= 0 {
220				return a, fmt.Errorf("--max-steps must be a positive integer")
221			}
222			a.MaxSteps = n
223		default:
224			if strings.HasPrefix(arg, "-") && arg != "-" {
225				return a, fmt.Errorf("unknown flag %q", arg)
226			}
227			positional = append(positional, arg)
228		}
229	}
230
231	if len(positional) > 0 {
232		a.Prompt = strings.Join(positional, " ")
233	}
234
235	if a.CWD == "" {
236		a.CWD, _ = os.Getwd()
237	}
238	return a, nil
239}
240
241// PrintHelp writes the help text to w (stderr by default).
242func PrintHelp(version string) {
243	fmt.Fprintf(os.Stderr, `zot %s — Yet another coding agent harness.
244
245usage:
246  zot                          interactive tui
247  zot "prompt"                 interactive, pre-filled prompt
248  zot -p "prompt"              print final text, exit
249  zot --json "prompt"          newline-delimited json events, exit
250  zot rpc                      json-rpc loop on stdin/stdout (see docs/rpc.md)
251  zot telegram-bot setup       configure a telegram bot (from BotFather)
252  zot telegram-bot run         foreground bridge (ctrl+c to stop)
253  zot telegram-bot start       background bridge (detached)
254  zot telegram-bot stop        stop the background bridge
255  zot telegram-bot logs [-f]   tail the background bridge's log
256  zot telegram-bot status      config + running state
257  zot telegram-bot reset       forget saved token
258  (short alias: zot tg ...)
259
260flags:
261  --provider  anthropic|openai
262  --model     model id (see --list-models)
263  --api-key   api key for this run (falls back to env and auth.json)
264  --base-url  override provider api base url (for testing/self-hosted)
265
266  --system-prompt TEXT         replace the default system prompt
267  --append-system-prompt TEXT  append to the system prompt (repeatable)
268  --reasoning low|medium|high  enable reasoning on supported models
269
270  -c, --continue               continue the most recent session for this cwd
271  -r, --resume                 pick a session to resume
272  --session PATH               resume a specific session file
273  --no-session                 do not read or write a session file
274
275  --cwd PATH                   treat PATH as the working directory
276  --no-tools                   disable all tools
277  --tools csv                  only enable the listed tools
278  -e, --ext PATH               load an extension from PATH for this run
279                               (repeatable; takes precedence over
280                               installed extensions of the same name)
281  --no-ext                     skip extension discovery for this run
282                               (--ext PATH still works on top, so
283                               --no-ext --ext ./x runs only x)
284  --no-skill                   skip skill discovery for this run,
285                               including built-in skills (no skill
286                               tool, no Available skills manifest)
287  --with-skills                load user-installed skills from
288                               $ZOT_HOME/skills/ + .zot/skills/ +
289                               .claude/skills/ + .agents/skills/.
290                               default: only built-in skills load
291
292  --no-yolo                    ask before running every tool call
293                               (interactive tui only; ignored with
294                               a stderr warning in -p / --json / rpc)
295
296  --max-steps N                agent loop iteration cap (default 50)
297  --list-models                print known models and exit
298  -h, --help                   this message
299  -v, --version                version info
300`, version)
301}

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