What would you like to be added?
A skills picker dialog opened by bare /skills that combines four jobs in one panel:
- Browse — see every skill loaded from project / user / extension / bundled scopes.
- Search — type to fuzzy-filter the visible list.
- Toggle — Space enables/disables a skill at workspace scope; Esc auto-saves and exits.
- Pick — Enter on a row drops
/<skill-name> into the input buffer (no auto-submit) so the user can review/edit before sending.
A new skills.disabled: string[] setting (UNION-merged across systemDefaults / user / workspace / system scopes) drives both the model-facing <available_skills> block and the /<skill-name> slash-command surface. Skills disabled at a higher scope show in a separate "locked" section in the dialog and are not toggleable from a lower scope.
Why is this needed?
Today /skills only lists skills. There's no first-class way to:
- Hide noise from the model. Long skill catalogs eat tokens in
<available_skills>. Codex (the sibling reference at https://github.com/openai/codex) ships exactly this picker — I built feature parity here.
- Discover & launch quickly. Tab-completion shows skill names but not their descriptions. The dialog renders name + description + scope side-by-side, with search.
- Reason about visibility across scopes. When an admin disables a skill at user/system scope, the user has no visual feedback inside the workspace today — they just don't see it. The locked section makes scope precedence explicit.
Issue #3696 ("comprehensive hot-reload system") is adjacent but doesn't cover skill-level enable/disable; this is a focused, smaller addition that doesn't depend on it.
Additional context
Implementation already on a branch + PR. Highlights:
- Live refresh, no restart —
Config.disabledSkillNamesProvider is a closure over LoadedSettings attached at construction time (so the first <available_skills> build at cold-start honors persisted disables in interactive, non-interactive, AND ACP sessions). On toggle: await reloadCommands(); await skillManager.notifyConfigChanged(); — strict serialization, since notifyConfigChanged reads modelInvocableCommandsProvider which is re-registered inside the reloadCommands effect.
- Single entry —
/skills opens the dialog directly. Typing /skil<Enter> from the auto-completion popup also opens the dialog (one keystroke), via a new SlashCommand.submitOnAccept opt-in flag that InputPrompt honors.
- Same-name MCP prompt safety —
SkillTool.refreshSkills excludes disabled skills from fileBasedSkillNames so a same-named MCP prompt resurfaces. validateToolParams and SkillToolInvocation.execute mirror the same commandExists → disabled-branch ordering so a disabled skill never shadows an MCP prompt during validation OR execution.
- Filter inside skill loaders only (
SkillCommandLoader, BundledSkillLoader) — never via the global disabledSlashCommands denylist, which would also hide a same-named built-in command.
- Untrusted workspace — refuses with a clear error rather than silently no-op'ing (workspace settings are dropped from the merged config when untrusted).
- i18n — all 9 supported locales (en / zh / zh-TW / ja / fr / de / pt / ru / ca) updated; key strings added to
MUST_TRANSLATE_KEYS to enforce parity going forward.
- Tests — 31+ regression tests covering refresh order, same-name MCP prompt protection (validate + execute), execute-side guard, listing/completion/filter, untrusted workspace, UNION-blocked behavior, locked-row semantics.
PR will link to this issue.
What would you like to be added?
A skills picker dialog opened by bare
/skillsthat combines four jobs in one panel:/<skill-name>into the input buffer (no auto-submit) so the user can review/edit before sending.A new
skills.disabled: string[]setting (UNION-merged acrosssystemDefaults/user/workspace/systemscopes) drives both the model-facing<available_skills>block and the/<skill-name>slash-command surface. Skills disabled at a higher scope show in a separate "locked" section in the dialog and are not toggleable from a lower scope.Why is this needed?
Today
/skillsonly lists skills. There's no first-class way to:<available_skills>. Codex (the sibling reference at https://github.com/openai/codex) ships exactly this picker — I built feature parity here.Issue #3696 ("comprehensive hot-reload system") is adjacent but doesn't cover skill-level enable/disable; this is a focused, smaller addition that doesn't depend on it.
Additional context
Implementation already on a branch + PR. Highlights:
Config.disabledSkillNamesProvideris a closure overLoadedSettingsattached at construction time (so the first<available_skills>build at cold-start honors persisted disables in interactive, non-interactive, AND ACP sessions). On toggle:await reloadCommands(); await skillManager.notifyConfigChanged();— strict serialization, sincenotifyConfigChangedreadsmodelInvocableCommandsProviderwhich is re-registered inside thereloadCommandseffect./skillsopens the dialog directly. Typing/skil<Enter>from the auto-completion popup also opens the dialog (one keystroke), via a newSlashCommand.submitOnAcceptopt-in flag thatInputPrompthonors.SkillTool.refreshSkillsexcludes disabled skills fromfileBasedSkillNamesso a same-named MCP prompt resurfaces.validateToolParamsandSkillToolInvocation.executemirror the samecommandExists→ disabled-branch ordering so a disabled skill never shadows an MCP prompt during validation OR execution.SkillCommandLoader,BundledSkillLoader) — never via the globaldisabledSlashCommandsdenylist, which would also hide a same-named built-in command.MUST_TRANSLATE_KEYSto enforce parity going forward.PR will link to this issue.