brewtools/skills/skill-toggle/SKILL.md
Disables/enables individual plugin skills (survives updates). Triggers: disable skill, enable skill, skill-toggle.
npx skillsauth add kochetkov-ma/claude-brewcode brewtools:skill-toggleInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
<instructions>Disable/enable individual plugin skills via the stable
skillOverridesmechanism in~/.claude/settings.json(Claude Code 2.1.115+). State survives plugin updates — no SessionStart reapply hook needed. See also:/brewtools:agent-togglefor agents (uses file-rename, different mechanism).
| Rule | Applies |
|------|---------|
| Every Bash call ends with && echo "OK ..." \|\| echo "FAILED ..." | ALL |
| Never use Write/Edit on ~/.claude/settings.json — use Bash + Node helper | ALL |
| This skill mutates skills only (skillOverrides) — never agents | ALL |
| Atomic writes via lockfile + temp + rename (handled by helper) | P2 |
Paths (substitute literally in Bash):
$CLAUDE_PLUGIN_ROOT/skills/skill-toggle/helpers/overrides.mjs~/.claude/settings.json (resolved to $HOME/.claude/settings.json)$CLAUDE_PLUGIN_DATA/toggle-state.json| Mode | Effect |
|------|--------|
| off | Fully disabled — invisible to user and LLM |
| user-invocable-only | Only via /plugin:skill slash; LLM cannot auto-invoke |
| name-only | Name visible in autocomplete, instructions/body not loaded |
| on | (Re-enable) — entry removed from settings.json |
Default for "disable" intent without explicit mode: off.
Full spec:
_shared/toggle/interactive-flow.md(phases I0-I4). Read on entry. This skill hardcodeskind='skill'.
Enter interactive flow when:
plugin:name (e.g. "отключи лишнее"), ORSkip interactive (go straight to P0 → P2 → P4) when op AND target both explicit: /brewtools:skill-toggle disable brewui:image-gen.
| Phase | Action | Tool |
|-------|--------|------|
| I0 | Decide branch from input shape | — |
| I1 | Op picker — single AskUserQuestion, options: status, disable, enable, list, pre-selected: disable | AskUserQuestion |
| I2 | Catalog one-liner — Bash+Node imports enumeratePlugins from $CLAUDE_PLUGIN_ROOT/skills/_shared/toggle/cache.mjs, emit AVAILABLE TO {OP} (N total): then space-separated plugin:name tokens (filter: disable→not in overrides; enable→in overrides; kind=skill) | Bash |
| I3 | Resolve + confirm: exact plugin:name or unique name→no confirm; fuzzy→AskUserQuestion [yes / pick different / cancel]; multiple→AskUserQuestion 2-4 options. For disable, also ask mode if not specified | AskUserQuestion |
| I4 | Execute (P2) then ALWAYS print current state | Bash |
Terminal ops (list, status) skip to I4 directly.
I4 status format (always printed):
DISABLED RIGHT NOW (via skillOverrides)
---------------------------------------
brewui:image-gen off
brewdoc:md-to-pdf user-invocable-only
(none) <-- if empty
ENABLED (M skills across P plugins)
Parse $ARGUMENTS (or NL prompt) into:
{ action: disable|enable|toggle|status|list, plugin?, skill?, mode?: off|user-invocable-only|name-only }
Rules:
disable = off. User can specify e.g. --mode=user-invocable-only or "make it slash-only".plugin:name (e.g., brewui:image-gen). Bare name → AskUserQuestion which plugin.status, list take no targets.--scope flag — skillOverrides is global-only (per Claude Code design).If ambiguous (no plugin prefix for disable/enable) → AskUserQuestion with candidate plugins from enumeratePlugins.
EXECUTE using Bash tool:
node --input-type=module -e "
import {enumeratePlugins, resolveTarget} from '$CLAUDE_PLUGIN_ROOT/skills/_shared/toggle/cache.mjs';
import fs from 'node:fs';
const p = enumeratePlugins().get('PLUGIN_NAME');
if (!p) { console.log(JSON.stringify({error:'plugin_not_installed', plugin:'PLUGIN_NAME'})); process.exit(0); }
const t = resolveTarget(p, 'skill', 'SKILL_NAME');
const exists = fs.existsSync(t.visible) || fs.existsSync(t.hidden);
if (!exists) { console.log(JSON.stringify({error:'skill_not_found', plugin:p.plugin, name:'SKILL_NAME'})); process.exit(0); }
console.log(JSON.stringify({plugin:p.plugin, latest:p.latest, name:'SKILL_NAME'}));
" && echo "OK validate" || echo "FAILED validate"
Replace PLUGIN_NAME, SKILL_NAME. On error → stop, report.
EXECUTE using Bash tool (disable):
node --input-type=module -e "
import {writeOverride} from '$CLAUDE_PLUGIN_ROOT/skills/skill-toggle/helpers/overrides.mjs';
const r = await writeOverride('PLUGIN','NAME','MODE');
console.log(JSON.stringify(r));
" && echo "OK override" || echo "FAILED override"
For disable → MODE ∈ off | user-invocable-only | name-only (default off).
For enable → MODE='on' (deletes the entry).
Substitute PLUGIN, NAME, MODE literally.
Persistence:
~/.claude/settings.jsonsurvives plugin updates — no SessionStart reapply hook needed for skill-toggle. The old file-rename approach (P3 in earlier versions) is removed. Plugin cache files are no longer touched by this skill.
Agent-toggle still uses file-rename (_shared/toggle/state.mjs + apply.mjs) — that flow is untouched.
EXECUTE using Bash tool:
node --input-type=module -e "
import {readOverrides} from '$CLAUDE_PLUGIN_ROOT/skills/skill-toggle/helpers/overrides.mjs';
const o = readOverrides();
console.log(JSON.stringify(o));
" && echo "OK verify" || echo "FAILED verify"
Confirm PLUGIN:NAME present (for disable) or absent (for enable). Render result table:
# Skill Toggle — <action>
| Plugin | Name | Mode | Action | File |
|--------|------|------|--------|------|
| brewui | image-gen | off | written | ~/.claude/settings.json |
> Restart session or `/reload-plugins` for the change to take effect.
> Persisted to ~/.claude/settings.json — survives plugin updates.
Reads both skillOverrides (current mechanism) AND legacy toggle-state.json (backwards visibility, read-only).
EXECUTE using Bash tool:
node --input-type=module -e "
import {listOverrides} from '$CLAUDE_PLUGIN_ROOT/skills/skill-toggle/helpers/overrides.mjs';
import {readState, globalStatePath, projectStatePath} from '$CLAUDE_PLUGIN_ROOT/skills/_shared/toggle/state.mjs';
const overrides = listOverrides();
const legacyG = readState(globalStatePath()).disabled || {};
const legacyP = readState(projectStatePath(process.cwd())).disabled || {};
const legacy = [];
for (const [k,v] of Object.entries({...legacyG, ...legacyP})) {
if (v && v.kind === 'skill') legacy.push({key:k, ...v, scope: legacyP[k] ? 'project' : 'global'});
}
console.log(JSON.stringify({overrides, legacy}));
" && echo "OK status" || echo "FAILED status"
Render two tables:
EXECUTE using Bash tool:
node --input-type=module -e "
import {enumeratePlugins, resolveTarget} from '$CLAUDE_PLUGIN_ROOT/skills/_shared/toggle/cache.mjs';
import {readOverrides} from '$CLAUDE_PLUGIN_ROOT/skills/skill-toggle/helpers/overrides.mjs';
import fs from 'node:fs';
import path from 'node:path';
const overrides = readOverrides();
const out = [];
for (const [plugin, e] of enumeratePlugins()) {
const dir = path.join(e.path, 'skills');
let entries = [];
try { entries = fs.readdirSync(dir, {withFileTypes:true}).filter(d=>d.isDirectory() && !d.name.startsWith('_')); } catch {}
for (const d of entries) {
const key = plugin + ':' + d.name;
out.push({plugin, name:d.name, version:e.latest, override: overrides[key] || null});
}
}
console.log(JSON.stringify(out));
" && echo "OK list" || echo "FAILED list"
Render grouped by plugin; mark rows with override mode.
</instructions>development
Publish HTML, markdown, text, any file, or a multi-file site to brewpage.app — free hosting with no sign-up. Paste text, share a file, upload a site, or host a temporary page and get an instant public URL to share a link. Asks namespace and password, returns the public URL. Triggers: publish, publish HTML, share link, share a link, share a file, upload to brewpage, host page, host a temporary page, host a website, free hosting, paste text, instant public URL, no sign-up, brewpage, publish site, upload site, upload directory, deploy site, сделай публичную ссылку, опубликуй.
tools
Toggles terse-output mode to cut preamble and filler. Triggers: think-short, be terse, think shorter.
devops
Publish content to brewpage.app — text, markdown, JSON, file, or multi-file site. Asks namespace and password, returns public URL. Triggers: publish, share link, upload to brewpage, host page, brewpage, publish site, upload site, upload directory, deploy site, сделай публичную ссылку, опубликуй.
data-ai
Generate AI images via OpenRouter, Z.ai, Imagen 4, DALL-E 3, anti-slop. Triggers: generate image, AI image, og image.