mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 21:36:31 +02:00
Previously --no-yolo in -p / --json / rpc modes auto-refused every
tool call. That made the flag dangerous to pass to scripts: a
single --no-yolo in a shell config or wrapper script would silently
break any tool-using prompt.
New behaviour:
- Default: every mode is yolo (tools run freely, no prompts).
- --no-yolo + interactive TUI: confirm dialog before each tool.
- --no-yolo + -p / --json / rpc: stderr warning and ignore the
flag. Tools run freely; scripts keep working.
The TUI confirm dialog and /yolo runtime toggle still work as
before. Also removed the unused wireNoYoloAutoRefuse helper and
simplified core.NewConfirmGate's doc comment.
301 lines
8.8 KiB
Go
301 lines
8.8 KiB
Go
package agent
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Mode is the CLI run mode.
|
|
type Mode string
|
|
|
|
const (
|
|
ModeInteractive Mode = "interactive"
|
|
ModePrint Mode = "print"
|
|
ModeJSON Mode = "json"
|
|
ModeRPC Mode = "rpc"
|
|
)
|
|
|
|
// Args holds parsed command-line options.
|
|
type Args struct {
|
|
Mode Mode
|
|
Provider string
|
|
Model string
|
|
APIKey string
|
|
|
|
BaseURL string // override provider base URL (for tests/self-hosted)
|
|
SystemPrompt string
|
|
AppendSystemPrompt []string
|
|
Reasoning string
|
|
|
|
Continue bool
|
|
Resume bool
|
|
Session string
|
|
NoSess bool
|
|
|
|
CWD string
|
|
NoTools bool
|
|
Tools []string
|
|
MaxSteps int
|
|
|
|
// Exts is a list of directory paths the user passed via --ext.
|
|
// Each must contain an extension.json. Loaded for one session
|
|
// only; never persisted. Take precedence over installed exts of
|
|
// the same name.
|
|
Exts []string
|
|
|
|
// NoExt disables extension discovery + spawn entirely for this
|
|
// run. --ext PATH still works (explicit beats implicit) so you
|
|
// can run "with only this one extension" via --no-ext --ext PATH.
|
|
NoExt bool
|
|
|
|
// NoSkill disables ALL skill discovery for this run, including
|
|
// the built-in skills compiled into the binary. The system
|
|
// prompt loses its "Available skills" manifest and the `skill`
|
|
// tool isn't registered. Useful for running zot without any
|
|
// extra context biasing the model.
|
|
NoSkill bool
|
|
|
|
// WithSkills opts into loading user-installed skills from
|
|
// $ZOT_HOME/skills/, .zot/skills/, .claude/skills/, and
|
|
// .agents/skills/. Without this flag only the built-in skills
|
|
// shipped with the zot binary are available, so a fresh install
|
|
// has a deterministic skill set regardless of what's lying
|
|
// around in the user's home directory.
|
|
WithSkills bool
|
|
|
|
// NoYolo turns on per-tool confirmation. Before each tool
|
|
// invocation the TUI prompts the user with the tool name + args
|
|
// and waits for an explicit yes/no. The user can also pick
|
|
// "always for this tool this session" or "always for anything
|
|
// this session" to stop being prompted again. Defaults off
|
|
// (yolo mode): tools run without asking.
|
|
//
|
|
// No effect in -p / --json / rpc modes, which have no
|
|
// interactive prompt. A warning is printed to stderr on startup
|
|
// so scripts know the flag is ignored, but tools still run
|
|
// freely so automated workflows keep working.
|
|
NoYolo bool
|
|
|
|
ListModels bool
|
|
Help bool
|
|
Version bool
|
|
|
|
Prompt string // concatenated positional args
|
|
}
|
|
|
|
// ParseArgs parses the process arguments (excluding argv[0]).
|
|
func ParseArgs(in []string) (Args, error) {
|
|
a := Args{Mode: ModeInteractive, MaxSteps: 50}
|
|
positional := []string{}
|
|
|
|
want := func(i *int, flag string) (string, error) {
|
|
*i++
|
|
if *i >= len(in) {
|
|
return "", fmt.Errorf("%s requires a value", flag)
|
|
}
|
|
return in[*i], nil
|
|
}
|
|
|
|
for i := 0; i < len(in); i++ {
|
|
arg := in[i]
|
|
switch arg {
|
|
case "-h", "--help":
|
|
a.Help = true
|
|
case "-v", "--version":
|
|
a.Version = true
|
|
case "-p", "--print":
|
|
a.Mode = ModePrint
|
|
case "--json":
|
|
a.Mode = ModeJSON
|
|
case "--rpc":
|
|
a.Mode = ModeRPC
|
|
case "-c", "--continue":
|
|
a.Continue = true
|
|
case "-r", "--resume":
|
|
a.Resume = true
|
|
case "--no-session":
|
|
a.NoSess = true
|
|
case "--no-tools":
|
|
a.NoTools = true
|
|
case "--list-models":
|
|
a.ListModels = true
|
|
case "--experimental-oauth":
|
|
// deprecated: subscription login is always available.
|
|
// accepted silently for backwards compatibility.
|
|
case "--provider":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.Provider = v
|
|
case "--model":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.Model = v
|
|
case "--api-key":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.APIKey = v
|
|
case "--base-url":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.BaseURL = v
|
|
case "--system-prompt":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.SystemPrompt = v
|
|
case "--append-system-prompt":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.AppendSystemPrompt = append(a.AppendSystemPrompt, v)
|
|
case "--ext", "-e":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
// Repeatable; each value is a directory containing an
|
|
// extension.json. Resolved to absolute later so paths like
|
|
// "." survive a later cwd change.
|
|
a.Exts = append(a.Exts, v)
|
|
case "--no-ext", "--no-extensions":
|
|
a.NoExt = true
|
|
case "--no-skill", "--no-skills":
|
|
a.NoSkill = true
|
|
case "--with-skills", "--with-skill":
|
|
a.WithSkills = true
|
|
case "--no-yolo":
|
|
a.NoYolo = true
|
|
case "--reasoning":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
switch strings.ToLower(v) {
|
|
case "", "low", "medium", "high":
|
|
a.Reasoning = strings.ToLower(v)
|
|
default:
|
|
return a, fmt.Errorf("--reasoning must be low|medium|high")
|
|
}
|
|
case "--session":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.Session = v
|
|
case "--cwd":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
a.CWD = v
|
|
case "--tools":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
for _, t := range strings.Split(v, ",") {
|
|
t = strings.TrimSpace(t)
|
|
if t != "" {
|
|
a.Tools = append(a.Tools, t)
|
|
}
|
|
}
|
|
case "--max-steps":
|
|
v, err := want(&i, arg)
|
|
if err != nil {
|
|
return a, err
|
|
}
|
|
var n int
|
|
if _, err := fmt.Sscanf(v, "%d", &n); err != nil || n <= 0 {
|
|
return a, fmt.Errorf("--max-steps must be a positive integer")
|
|
}
|
|
a.MaxSteps = n
|
|
default:
|
|
if strings.HasPrefix(arg, "-") && arg != "-" {
|
|
return a, fmt.Errorf("unknown flag %q", arg)
|
|
}
|
|
positional = append(positional, arg)
|
|
}
|
|
}
|
|
|
|
if len(positional) > 0 {
|
|
a.Prompt = strings.Join(positional, " ")
|
|
}
|
|
|
|
if a.CWD == "" {
|
|
a.CWD, _ = os.Getwd()
|
|
}
|
|
return a, nil
|
|
}
|
|
|
|
// PrintHelp writes the help text to w (stderr by default).
|
|
func PrintHelp(version string) {
|
|
fmt.Fprintf(os.Stderr, `zot %s — Yet another coding agent harness.
|
|
|
|
usage:
|
|
zot interactive tui
|
|
zot "prompt" interactive, pre-filled prompt
|
|
zot -p "prompt" print final text, exit
|
|
zot --json "prompt" newline-delimited json events, exit
|
|
zot rpc json-rpc loop on stdin/stdout (see docs/rpc.md)
|
|
zot telegram-bot setup configure a telegram bot (from BotFather)
|
|
zot telegram-bot run foreground bridge (ctrl+c to stop)
|
|
zot telegram-bot start background bridge (detached)
|
|
zot telegram-bot stop stop the background bridge
|
|
zot telegram-bot logs [-f] tail the background bridge's log
|
|
zot telegram-bot status config + running state
|
|
zot telegram-bot reset forget saved token
|
|
(short alias: zot tg ...)
|
|
|
|
flags:
|
|
--provider anthropic|openai
|
|
--model model id (see --list-models)
|
|
--api-key api key for this run (falls back to env and auth.json)
|
|
--base-url override provider api base url (for testing/self-hosted)
|
|
|
|
--system-prompt TEXT replace the default system prompt
|
|
--append-system-prompt TEXT append to the system prompt (repeatable)
|
|
--reasoning low|medium|high enable reasoning on supported models
|
|
|
|
-c, --continue continue the most recent session for this cwd
|
|
-r, --resume pick a session to resume
|
|
--session PATH resume a specific session file
|
|
--no-session do not read or write a session file
|
|
|
|
--cwd PATH treat PATH as the working directory
|
|
--no-tools disable all tools
|
|
--tools csv only enable the listed tools
|
|
-e, --ext PATH load an extension from PATH for this run
|
|
(repeatable; takes precedence over
|
|
installed extensions of the same name)
|
|
--no-ext skip extension discovery for this run
|
|
(--ext PATH still works on top, so
|
|
--no-ext --ext ./x runs only x)
|
|
--no-skill skip skill discovery for this run,
|
|
including built-in skills (no skill
|
|
tool, no Available skills manifest)
|
|
--with-skills load user-installed skills from
|
|
$ZOT_HOME/skills/ + .zot/skills/ +
|
|
.claude/skills/ + .agents/skills/.
|
|
default: only built-in skills load
|
|
|
|
--no-yolo ask before running every tool call
|
|
(interactive tui only; ignored with
|
|
a stderr warning in -p / --json / rpc)
|
|
|
|
--max-steps N agent loop iteration cap (default 50)
|
|
--list-models print known models and exit
|
|
-h, --help this message
|
|
-v, --version version info
|
|
`, version)
|
|
}
|