v0.0.30 2cffe04

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

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 477

↑↓ moveenter openesc close