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