detached e9ffc74

feat(skills): --no-skill flag to disable all skills (including built-ins)

Mirrors --no-ext: one flag that skips skill discovery + the
`skill` tool registration entirely for one run. Useful for
clean-room runs where you want zero extra context biasing the
model.

Defaults are unchanged:
  - user-installed skills under $ZOT_HOME/skills/, .zot/skills/,
    .claude/skills/, .agents/skills/ load normally
  - built-in skills compiled into the binary (currently the
    write-zot-extension authoring guide) load normally
  - both groups appear in the system-prompt manifest and are
    loadable via the `skill` tool

With --no-skill (or --no-skills):
  - skills.Discover() is not called
  - the `skill` tool is not registered
  - no "Available skills" addendum is appended to the system
    prompt
  - /skills picker is empty

Wired into both interactive and rpc modes via the existing
SkillSnapshot wiring (returns nil when args.NoSkill is set, so
the picker also stays empty).

End-to-end verified live:
  default            : tool list includes "skill"
  --no-skill         : tool list is read, write, edit, bash only
                       (no skill, no extra context manifest)
commit
e9ffc744428f259d74b90d9c5c3c0dee87b41699
author
patriceckhart · · 2026-04-19 13:58 UTC
parent
2cffe04
signature
unsigned
refs
tag: v0.0.31
3 files · +37 -12
M internal/agent/args.go +12 -0
@@ -49,6 +49,13 @@ type Args struct {
4949 // can run "with only this one extension" via --no-ext --ext PATH.
5050 NoExt bool
5151
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+
5259 ListModels bool
5360 Help bool
5461 Version bool
@@ -142,6 +149,8 @@ func ParseArgs(in []string) (Args, error) {
142149 a.Exts = append(a.Exts, v)
143150 case "--no-ext", "--no-extensions":
144151 a.NoExt = true
152+ case "--no-skill", "--no-skills":
153+ a.NoSkill = true
145154 case "--reasoning":
146155 v, err := want(&i, arg)
147156 if err != nil {
@@ -247,6 +256,9 @@ flags:
247256 --no-ext skip extension discovery for this run
248257 (--ext PATH still works on top, so
249258 --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)
250262
251263 --max-steps N agent loop iteration cap (default 50)
252264 --list-models print known models and exit
@@ -49,6 +49,13 @@ type Args struct {
49 // can run "with only this one extension" via --no-ext --ext PATH.49 // can run "with only this one extension" via --no-ext --ext PATH.
50 NoExt bool50 NoExt bool
51 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+
52 ListModels bool59 ListModels bool
53 Help bool60 Help bool
54 Version bool61 Version bool
@@ -142,6 +149,8 @@ func ParseArgs(in []string) (Args, error) {
142 a.Exts = append(a.Exts, v)149 a.Exts = append(a.Exts, v)
143 case "--no-ext", "--no-extensions":150 case "--no-ext", "--no-extensions":
144 a.NoExt = true151 a.NoExt = true
152+ case "--no-skill", "--no-skills":
153+ a.NoSkill = true
145 case "--reasoning":154 case "--reasoning":
146 v, err := want(&i, arg)155 v, err := want(&i, arg)
147 if err != nil {156 if err != nil {
@@ -247,6 +256,9 @@ flags:
247 --no-ext skip extension discovery for this run256 --no-ext skip extension discovery for this run
248 (--ext PATH still works on top, so257 (--ext PATH still works on top, so
249 --no-ext --ext ./x runs only x)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)
250 262
251 --max-steps N agent loop iteration cap (default 50)263 --max-steps N agent loop iteration cap (default 50)
252 --list-models print known models and exit264 --list-models print known models and exit
M internal/agent/build.go +21 -12
@@ -179,18 +179,27 @@ func Resolve(args Args, requireCred bool) (Resolved, error) {
179179 sandbox := tools.NewSandbox(args.CWD)
180180 reg := buildToolRegistry(args, args.CWD, sandbox)
181181
182- // Skill discovery: scan project + global locations and, if any
183- // SKILL.md files were found, register the on-demand `skill`
184- // loader tool plus a system-prompt manifest so the model knows
185- // what's available.
186- homeDir, _ := os.UserHomeDir()
187- discovered, _ := skills.Discover(ZotHome(), args.CWD, homeDir)
188- var skillTool *skills.Tool
189- var skillAddendum string
190- if len(discovered) > 0 {
191- skillTool = skills.NewTool(discovered)
192- reg[skillTool.Name()] = skillTool
193- skillAddendum = skills.SystemPromptAddendum(discovered)
182+ // Skill discovery: scan project + global locations + built-in
183+ // skills shipped with the binary. If any are found, register
184+ // the on-demand `skill` loader tool plus a system-prompt
185+ // manifest so the model knows what's available.
186+ //
187+ // --no-skill bypasses the entire mechanism: no manifest in the
188+ // system prompt, no `skill` tool in the registry. Useful for a
189+ // clean-room run with zero extra context biasing the model.
190+ var (
191+ discovered []*skills.Skill
192+ skillTool *skills.Tool
193+ skillAddendum string
194+ )
195+ if !args.NoSkill {
196+ homeDir, _ := os.UserHomeDir()
197+ discovered, _ = skills.Discover(ZotHome(), args.CWD, homeDir)
198+ if len(discovered) > 0 {
199+ skillTool = skills.NewTool(discovered)
200+ reg[skillTool.Name()] = skillTool
201+ skillAddendum = skills.SystemPromptAddendum(discovered)
202+ }
194203 }
195204 _ = skillTool
196205
@@ -179,18 +179,27 @@ func Resolve(args Args, requireCred bool) (Resolved, error) {
179 sandbox := tools.NewSandbox(args.CWD)179 sandbox := tools.NewSandbox(args.CWD)
180 reg := buildToolRegistry(args, args.CWD, sandbox)180 reg := buildToolRegistry(args, args.CWD, sandbox)
181 181
182- // Skill discovery: scan project + global locations and, if any182+ // Skill discovery: scan project + global locations + built-in
183- // SKILL.md files were found, register the on-demand `skill`183+ // skills shipped with the binary. If any are found, register
184- // loader tool plus a system-prompt manifest so the model knows184+ // the on-demand `skill` loader tool plus a system-prompt
185- // what's available.185+ // manifest so the model knows what's available.
186- homeDir, _ := os.UserHomeDir()186+ //
187- discovered, _ := skills.Discover(ZotHome(), args.CWD, homeDir)187+ // --no-skill bypasses the entire mechanism: no manifest in the
188- var skillTool *skills.Tool188+ // system prompt, no `skill` tool in the registry. Useful for a
189- var skillAddendum string189+ // clean-room run with zero extra context biasing the model.
190- if len(discovered) > 0 {190+ var (
191- skillTool = skills.NewTool(discovered)191+ discovered []*skills.Skill
192- reg[skillTool.Name()] = skillTool192+ skillTool *skills.Tool
193- skillAddendum = skills.SystemPromptAddendum(discovered)193+ skillAddendum string
194+ )
195+ if !args.NoSkill {
196+ homeDir, _ := os.UserHomeDir()
197+ discovered, _ = skills.Discover(ZotHome(), args.CWD, homeDir)
198+ if len(discovered) > 0 {
199+ skillTool = skills.NewTool(discovered)
200+ reg[skillTool.Name()] = skillTool
201+ skillAddendum = skills.SystemPromptAddendum(discovered)
202+ }
194 }203 }
195 _ = skillTool204 _ = skillTool
196 205
M internal/agent/cli.go +4 -0
@@ -408,6 +408,10 @@ func runInteractive(ctx context.Context, args Args, version string) error {
408408 LoadSession: loadSession,
409409 Extensions: extMgr,
410410 SkillSnapshot: func() []*skills.Skill {
411+ if args.NoSkill {
412+ // --no-skill: nothing for the picker to show.
413+ return nil
414+ }
411415 // Re-discover so the picker reflects edits made during
412416 // the session. Cheap; SKILL.md files are small. Filter
413417 // out built-in skills — they're hidden from user-facing
@@ -408,6 +408,10 @@ func runInteractive(ctx context.Context, args Args, version string) error {
408 LoadSession: loadSession,408 LoadSession: loadSession,
409 Extensions: extMgr,409 Extensions: extMgr,
410 SkillSnapshot: func() []*skills.Skill {410 SkillSnapshot: func() []*skills.Skill {
411+ if args.NoSkill {
412+ // --no-skill: nothing for the picker to show.
413+ return nil
414+ }
411 // Re-discover so the picker reflects edits made during415 // Re-discover so the picker reflects edits made during
412 // the session. Cheap; SKILL.md files are small. Filter416 // the session. Cheap; SKILL.md files are small. Filter
413 // out built-in skills — they're hidden from user-facing417 // out built-in skills — they're hidden from user-facing

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