Load user skills by default
- commit
- 5293277d36b82ecfbd5682da82a6078ad8f454b1
- author
- patriceckhart · · 2026-05-24 18:12 UTC
- parent
- 90d877f
- signature
- unsigned
- refs
- tag: v0.1.95
5 files · +14 -19
M README.md +2 -3
| @@ -107,7 +107,7 @@ $ZOT_HOME/ | |||
| 107 | 107 | ├── sessions/ # jsonl transcripts, one dir per cwd | |
| 108 | 108 | ├── models-cache.json # live /v1/models discovery cache (6h ttl) | |
| 109 | 109 | ├── SYSTEM.md # optional: replaces the default system prompt | |
| 110 | - | ├── skills/ # optional: user SKILL.md files (opt in with --with-skills) | |
| 110 | + | ├── skills/ # optional: user SKILL.md files | |
| 111 | 111 | ├── extensions/ # installed extensions, one dir per extension | |
| 112 | 112 | └── logs/ # app log files | |
| 113 | 113 | ``` | |
| @@ -152,7 +152,6 @@ zot --help | |||
| 152 | 152 | | `--max-steps <n>` | Cap agent loop iterations (default 50). | | |
| 153 | 153 | | `-e`, `--ext <path>` | Load an extension from `<path>` for this run (repeatable; wins against installed extensions of the same name). | | |
| 154 | 154 | | `--no-ext` | Skip extension discovery for this run. `--ext` still works on top, so `--no-ext --ext ./x` runs only `x`. | | |
| 155 | - | | `--with-skills` | Also load user-installed skills. Without this, only the built-in skills shipped in the binary are loaded. | | |
| 156 | 155 | | `--no-skill` | Disable all skills, including built-ins. No `skill` tool is registered and the system prompt has no skill manifest. | | |
| 157 | 156 | | `--no-yolo` | Confirm every tool call before it runs (interactive TUI only). A dialog shows the tool name and a one-line preview of its args with four choices: yes, yes-always-this-tool-this-session, yes-always-this-session, no. Ignored with a stderr warning in print / json / rpc modes, where tools still run freely so scripts and automation keep working. | | |
| 158 | 157 | ||
| @@ -596,7 +595,7 @@ For development, point `zot --ext <path>` at a working directory and skip the in | |||
| 596 | 595 | ||
| 597 | 596 | A skill is a per-folder `SKILL.md` file with a YAML frontmatter header. zot discovers skills at startup, surfaces their names in the system prompt, and exposes a built-in `skill` tool the model uses to load the body on demand. | |
| 598 | 597 | ||
| 599 | - | By default only the built-in skills shipped with the zot binary are loaded. Pass `--with-skills` to also load user-installed skills from: | |
| 598 | + | By default zot loads built-in skills plus user-installed skills from: | |
| 600 | 599 | ||
| 601 | 600 | - `./.zot/skills/<name>/SKILL.md` (project) | |
| 602 | 601 | - `$ZOT_HOME/skills/<name>/SKILL.md` (global) | |
| @@ -107,7 +107,7 @@ $ZOT_HOME/ | |||||
| 107 | ├── sessions/ # jsonl transcripts, one dir per cwd | 107 | ├── sessions/ # jsonl transcripts, one dir per cwd | ||
| 108 | ├── models-cache.json # live /v1/models discovery cache (6h ttl) | 108 | ├── models-cache.json # live /v1/models discovery cache (6h ttl) | ||
| 109 | ├── SYSTEM.md # optional: replaces the default system prompt | 109 | ├── SYSTEM.md # optional: replaces the default system prompt | ||
| 110 | - | ├── skills/ # optional: user SKILL.md files (opt in with --with-skills) | 110 | + | ├── skills/ # optional: user SKILL.md files |
| 111 | ├── extensions/ # installed extensions, one dir per extension | 111 | ├── extensions/ # installed extensions, one dir per extension | ||
| 112 | └── logs/ # app log files | 112 | └── logs/ # app log files | ||
| 113 | ``` | 113 | ``` | ||
| @@ -152,7 +152,6 @@ zot --help | |||||
| 152 | | `--max-steps <n>` | Cap agent loop iterations (default 50). | | 152 | | `--max-steps <n>` | Cap agent loop iterations (default 50). | | ||
| 153 | | `-e`, `--ext <path>` | Load an extension from `<path>` for this run (repeatable; wins against installed extensions of the same name). | | 153 | | `-e`, `--ext <path>` | Load an extension from `<path>` for this run (repeatable; wins against installed extensions of the same name). | | ||
| 154 | | `--no-ext` | Skip extension discovery for this run. `--ext` still works on top, so `--no-ext --ext ./x` runs only `x`. | | 154 | | `--no-ext` | Skip extension discovery for this run. `--ext` still works on top, so `--no-ext --ext ./x` runs only `x`. | | ||
| 155 | - | | `--with-skills` | Also load user-installed skills. Without this, only the built-in skills shipped in the binary are loaded. | | |||
| 156 | | `--no-skill` | Disable all skills, including built-ins. No `skill` tool is registered and the system prompt has no skill manifest. | | 155 | | `--no-skill` | Disable all skills, including built-ins. No `skill` tool is registered and the system prompt has no skill manifest. | | ||
| 157 | | `--no-yolo` | Confirm every tool call before it runs (interactive TUI only). A dialog shows the tool name and a one-line preview of its args with four choices: yes, yes-always-this-tool-this-session, yes-always-this-session, no. Ignored with a stderr warning in print / json / rpc modes, where tools still run freely so scripts and automation keep working. | | 156 | | `--no-yolo` | Confirm every tool call before it runs (interactive TUI only). A dialog shows the tool name and a one-line preview of its args with four choices: yes, yes-always-this-tool-this-session, yes-always-this-session, no. Ignored with a stderr warning in print / json / rpc modes, where tools still run freely so scripts and automation keep working. | | ||
| 158 | 157 | ||||
| @@ -596,7 +595,7 @@ For development, point `zot --ext <path>` at a working directory and skip the in | |||||
| 596 | 595 | ||||
| 597 | A skill is a per-folder `SKILL.md` file with a YAML frontmatter header. zot discovers skills at startup, surfaces their names in the system prompt, and exposes a built-in `skill` tool the model uses to load the body on demand. | 596 | A skill is a per-folder `SKILL.md` file with a YAML frontmatter header. zot discovers skills at startup, surfaces their names in the system prompt, and exposes a built-in `skill` tool the model uses to load the body on demand. | ||
| 598 | 597 | ||||
| 599 | - | By default only the built-in skills shipped with the zot binary are loaded. Pass `--with-skills` to also load user-installed skills from: | 598 | + | By default zot loads built-in skills plus user-installed skills from: |
| 600 | 599 | ||||
| 601 | - `./.zot/skills/<name>/SKILL.md` (project) | 600 | - `./.zot/skills/<name>/SKILL.md` (project) | ||
| 602 | - `$ZOT_HOME/skills/<name>/SKILL.md` (global) | 601 | - `$ZOT_HOME/skills/<name>/SKILL.md` (global) | ||
M internal/agent/args.go +5 -7
| @@ -66,12 +66,10 @@ type Args struct { | |||
| 66 | 66 | // extra context biasing the model. | |
| 67 | 67 | NoSkill bool | |
| 68 | 68 | ||
| 69 | - | // WithSkills opts into loading user-installed skills from | |
| 69 | + | // WithSkills controls loading user-installed skills from | |
| 70 | 70 | // $ZOT_HOME/skills/, .zot/skills/, .claude/skills/, and | |
| 71 | - | // .agents/skills/. Without this flag only the built-in skills | |
| 72 | - | // shipped with the zot binary are available, so a fresh install | |
| 73 | - | // has a deterministic skill set regardless of what's lying | |
| 74 | - | // around in the user's home directory. | |
| 71 | + | // .agents/skills/. It defaults to true; --no-skill disables all | |
| 72 | + | // skill discovery, including built-ins. | |
| 75 | 73 | WithSkills bool | |
| 76 | 74 | ||
| 77 | 75 | // NoYolo turns on per-tool confirmation. Before each tool | |
| @@ -101,7 +99,7 @@ type Args struct { | |||
| 101 | 99 | ||
| 102 | 100 | // ParseArgs parses the process arguments (excluding argv[0]). | |
| 103 | 101 | func ParseArgs(in []string) (Args, error) { | |
| 104 | - | a := Args{Mode: ModeInteractive, MaxSteps: 0} | |
| 102 | + | a := Args{Mode: ModeInteractive, MaxSteps: 0, WithSkills: true} | |
| 105 | 103 | positional := []string{} | |
| 106 | 104 | ||
| 107 | 105 | want := func(i *int, flag string) (string, error) { | |
| @@ -188,6 +186,7 @@ func ParseArgs(in []string) (Args, error) { | |||
| 188 | 186 | case "--no-skill", "--no-skills": | |
| 189 | 187 | a.NoSkill = true | |
| 190 | 188 | case "--with-skills", "--with-skill": | |
| 189 | + | // Deprecated no-op: user skills are loaded by default. | |
| 191 | 190 | a.WithSkills = true | |
| 192 | 191 | case "--no-yolo": | |
| 193 | 192 | a.NoYolo = true | |
| @@ -378,7 +377,6 @@ func PrintHelp(version string) { | |||
| 378 | 377 | row{"--no-yolo", "ask before running every tool call"}, | |
| 379 | 378 | row{"--no-ext", "skip extension discovery for this run"}, | |
| 380 | 379 | row{"--no-skill", "skip all skill discovery for this run"}, | |
| 381 | - | row{"--with-skills", "load user-installed skills in addition to built-ins"}, | |
| 382 | 380 | ) | |
| 383 | 381 | section("misc", | |
| 384 | 382 | row{"--max-steps N", "agent loop iteration cap (default: unlimited)"}, | |
| @@ -66,12 +66,10 @@ type Args struct { | |||||
| 66 | // extra context biasing the model. | 66 | // extra context biasing the model. | ||
| 67 | NoSkill bool | 67 | NoSkill bool | ||
| 68 | 68 | ||||
| 69 | - | // WithSkills opts into loading user-installed skills from | 69 | + | // WithSkills controls loading user-installed skills from |
| 70 | // $ZOT_HOME/skills/, .zot/skills/, .claude/skills/, and | 70 | // $ZOT_HOME/skills/, .zot/skills/, .claude/skills/, and | ||
| 71 | - | // .agents/skills/. Without this flag only the built-in skills | 71 | + | // .agents/skills/. It defaults to true; --no-skill disables all |
| 72 | - | // shipped with the zot binary are available, so a fresh install | 72 | + | // skill discovery, including built-ins. |
| 73 | - | // has a deterministic skill set regardless of what's lying | |||
| 74 | - | // around in the user's home directory. | |||
| 75 | WithSkills bool | 73 | WithSkills bool | ||
| 76 | 74 | ||||
| 77 | // NoYolo turns on per-tool confirmation. Before each tool | 75 | // NoYolo turns on per-tool confirmation. Before each tool | ||
| @@ -101,7 +99,7 @@ type Args struct { | |||||
| 101 | 99 | ||||
| 102 | // ParseArgs parses the process arguments (excluding argv[0]). | 100 | // ParseArgs parses the process arguments (excluding argv[0]). | ||
| 103 | func ParseArgs(in []string) (Args, error) { | 101 | func ParseArgs(in []string) (Args, error) { | ||
| 104 | - | a := Args{Mode: ModeInteractive, MaxSteps: 0} | 102 | + | a := Args{Mode: ModeInteractive, MaxSteps: 0, WithSkills: true} |
| 105 | positional := []string{} | 103 | positional := []string{} | ||
| 106 | 104 | ||||
| 107 | want := func(i *int, flag string) (string, error) { | 105 | want := func(i *int, flag string) (string, error) { | ||
| @@ -188,6 +186,7 @@ func ParseArgs(in []string) (Args, error) { | |||||
| 188 | case "--no-skill", "--no-skills": | 186 | case "--no-skill", "--no-skills": | ||
| 189 | a.NoSkill = true | 187 | a.NoSkill = true | ||
| 190 | case "--with-skills", "--with-skill": | 188 | case "--with-skills", "--with-skill": | ||
| 189 | + | // Deprecated no-op: user skills are loaded by default. | |||
| 191 | a.WithSkills = true | 190 | a.WithSkills = true | ||
| 192 | case "--no-yolo": | 191 | case "--no-yolo": | ||
| 193 | a.NoYolo = true | 192 | a.NoYolo = true | ||
| @@ -378,7 +377,6 @@ func PrintHelp(version string) { | |||||
| 378 | row{"--no-yolo", "ask before running every tool call"}, | 377 | row{"--no-yolo", "ask before running every tool call"}, | ||
| 379 | row{"--no-ext", "skip extension discovery for this run"}, | 378 | row{"--no-ext", "skip extension discovery for this run"}, | ||
| 380 | row{"--no-skill", "skip all skill discovery for this run"}, | 379 | row{"--no-skill", "skip all skill discovery for this run"}, | ||
| 381 | - | row{"--with-skills", "load user-installed skills in addition to built-ins"}, | |||
| 382 | ) | 380 | ) | ||
| 383 | section("misc", | 381 | section("misc", | ||
| 384 | row{"--max-steps N", "agent loop iteration cap (default: unlimited)"}, | 382 | row{"--max-steps N", "agent loop iteration cap (default: unlimited)"}, | ||
M internal/agent/cli.go +1 -3
| @@ -977,9 +977,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||
| 977 | 977 | // out built-in skills — they're hidden from user-facing | |
| 978 | 978 | // surfaces because they're implementation detail; the | |
| 979 | 979 | // model still sees them through the system-prompt | |
| 980 | - | // manifest and the skill tool. User skills only appear | |
| 981 | - | // when --with-skills is set; without it the picker shows | |
| 982 | - | // nothing but the model still has the built-ins. | |
| 980 | + | // manifest and the skill tool. | |
| 983 | 981 | userHome, _ := os.UserHomeDir() | |
| 984 | 982 | list, _ := skills.Discover(ZotHome(), r.CWD, userHome, args.WithSkills) | |
| 985 | 983 | return skills.VisibleSkills(list) | |
| @@ -977,9 +977,7 @@ func runInteractive(ctx context.Context, args Args, version string) error { | |||||
| 977 | // out built-in skills — they're hidden from user-facing | 977 | // out built-in skills — they're hidden from user-facing | ||
| 978 | // surfaces because they're implementation detail; the | 978 | // surfaces because they're implementation detail; the | ||
| 979 | // model still sees them through the system-prompt | 979 | // model still sees them through the system-prompt | ||
| 980 | - | // manifest and the skill tool. User skills only appear | 980 | + | // manifest and the skill tool. |
| 981 | - | // when --with-skills is set; without it the picker shows | |||
| 982 | - | // nothing but the model still has the built-ins. | |||
| 983 | userHome, _ := os.UserHomeDir() | 981 | userHome, _ := os.UserHomeDir() | ||
| 984 | list, _ := skills.Discover(ZotHome(), r.CWD, userHome, args.WithSkills) | 982 | list, _ := skills.Discover(ZotHome(), r.CWD, userHome, args.WithSkills) | ||
| 985 | return skills.VisibleSkills(list) | 983 | return skills.VisibleSkills(list) | ||
M internal/agent/modes/skills_dialog.go +2 -2
| @@ -99,8 +99,8 @@ func (d *skillsDialog) Render(th tui.Theme, width int) []string { | |||
| 99 | 99 | ||
| 100 | 100 | out := []string{frameHeader(th, "skills (enter to view, esc to close)", width)} | |
| 101 | 101 | if len(d.skills) == 0 { | |
| 102 | - | out = append(out, " "+th.FG256(th.Muted, "no skills found")) | |
| 103 | - | out = append(out, " "+th.FG256(th.Muted, "see docs/skills.md to add some")) | |
| 102 | + | out = append(out, " "+th.FG256(th.Muted, "no user skills loaded")) | |
| 103 | + | out = append(out, " "+th.FG256(th.Muted, "add SKILL.md under $ZOT_HOME/skills, .zot/skills, .claude/skills, or .agents/skills")) | |
| 104 | 104 | out = append(out, frameRule(th, width)) | |
| 105 | 105 | return out | |
| 106 | 106 | } | |
| @@ -99,8 +99,8 @@ func (d *skillsDialog) Render(th tui.Theme, width int) []string { | |||||
| 99 | 99 | ||||
| 100 | out := []string{frameHeader(th, "skills (enter to view, esc to close)", width)} | 100 | out := []string{frameHeader(th, "skills (enter to view, esc to close)", width)} | ||
| 101 | if len(d.skills) == 0 { | 101 | if len(d.skills) == 0 { | ||
| 102 | - | out = append(out, " "+th.FG256(th.Muted, "no skills found")) | 102 | + | out = append(out, " "+th.FG256(th.Muted, "no user skills loaded")) |
| 103 | - | out = append(out, " "+th.FG256(th.Muted, "see docs/skills.md to add some")) | 103 | + | out = append(out, " "+th.FG256(th.Muted, "add SKILL.md under $ZOT_HOME/skills, .zot/skills, .claude/skills, or .agents/skills")) |
| 104 | out = append(out, frameRule(th, width)) | 104 | out = append(out, frameRule(th, width)) | ||
| 105 | return out | 105 | return out | ||
| 106 | } | 106 | } | ||
M internal/skills/skills.go +4 -4
| @@ -83,10 +83,10 @@ func VisibleSkills(in []*Skill) []*Skill { | |||
| 83 | 83 | return out | |
| 84 | 84 | } | |
| 85 | 85 | ||
| 86 | - | // Discover returns the merged skill set. By default this is just | |
| 87 | - | // the built-in skills compiled into the zot binary; user-installed | |
| 88 | - | // SKILL.md files are NOT loaded unless includeUser is true. Users | |
| 89 | - | // opt in via the `--with-skills` flag. | |
| 86 | + | // Discover returns the merged skill set. When includeUser is true, | |
| 87 | + | // user-installed SKILL.md files are loaded before built-ins. Callers | |
| 88 | + | // normally pass true; --no-skill skips discovery entirely before this | |
| 89 | + | // function is called. | |
| 90 | 90 | // | |
| 91 | 91 | // First-match-wins per name; the order matches the priority list | |
| 92 | 92 | // in the package doc (project-local before global before claude- | |
| @@ -83,10 +83,10 @@ func VisibleSkills(in []*Skill) []*Skill { | |||||
| 83 | return out | 83 | return out | ||
| 84 | } | 84 | } | ||
| 85 | 85 | ||||
| 86 | - | // Discover returns the merged skill set. By default this is just | 86 | + | // Discover returns the merged skill set. When includeUser is true, |
| 87 | - | // the built-in skills compiled into the zot binary; user-installed | 87 | + | // user-installed SKILL.md files are loaded before built-ins. Callers |
| 88 | - | // SKILL.md files are NOT loaded unless includeUser is true. Users | 88 | + | // normally pass true; --no-skill skips discovery entirely before this |
| 89 | - | // opt in via the `--with-skills` flag. | 89 | + | // function is called. |
| 90 | // | 90 | // | ||
| 91 | // First-match-wins per name; the order matches the priority list | 91 | // First-match-wins per name; the order matches the priority list | ||
| 92 | // in the package doc (project-local before global before claude- | 92 | // in the package doc (project-local before global before claude- | ||