Output style from a skills-directory plugin silently modifies every session's system prompt while /config reports "default"
Summary
A skills-directory plugin (a directory in ~/.claude/skills/, loaded in place) whose output-styles/*.md declares force-for-plugin: true sets the output style for every session, taking precedence over settings.json, with:
- no output style selected by the user,
- no
outputStylekey required in any settings file, - and
/configcontinuing to report Output style: default.
The active style also changes silently when another such directory appears. There is no surface in the product that reveals any of this — I found it only because an agent mentioned its own system prompt contents mid-task, in a session about something unrelated.
Both plugins were generated by the built-in scaffolder — claude plugin init cc-override --with skills agents hooks output-style — which emits force-for-plugin: true by default in a file whose body is an unfilled TODO: placeholder. So a plugin still being authored, created with a first-party command, modifies every session the moment its directory exists.
The usual way to keep a skills-directory plugin from taking effect globally is to leave it un-enabled. force-for-plugin defeats that: neither plugin below was ever enabled, and both still forced their style.
What I observed
Two skills-directory plugins on this machine, both scaffolded, neither finished. Their generated output styles are byte-identical apart from the name:
---
name: slide-bundle
description: TODO — one line shown in the Output style picker in /config
force-for-plugin: true
keep-coding-instructions: true
---
TODO: the style prompt. This is appended to Claude's system prompt while the
style is active. With force-for-plugin: true, the style applies automatically
when this plugin is enabled.
Session A — only slide-bundle existed. Asked Claude to quote its Output Style section; it returned:
# Output Style: slide-bundle:slide-bundle
TODO: the style prompt. This is appended to Claude's system prompt while the
style is active. ...
Session B — after cc-override/ was created (same scaffold, same flag), same question:
# Output Style: cc-override:cc-override
The globally active output style changed because a directory appeared on disk. No command was run, no setting was written, no plugin was enabled.
What each surface reports
| Surface | Reports | Actual |
|---|---|---|
| ~/.claude/settings.json → outputStyle | absent | — |
| ~/.claude/settings.local.json → outputStyle | absent | — |
| project .claude/settings.local.json → outputStyle | absent | — |
| /config → Output style | default | cc-override:cc-override active |
| claude plugin list | ✔ loaded (correct) | loaded — but no indication it forces a style |
claude plugin list is the one surface that gets it right: it shows the plugin as loaded, in a dedicated "Skills-directory plugins" section. But it reports load state, not what the plugin is doing — nothing there says this plugin is rewriting the system prompt.
The gap is /config, whose entire purpose is reporting which output style is in effect. It says default while a forced style is in the prompt.
Why this is serious
An output style is appended to the system prompt. That is the highest-leverage text in a session.
In my case the injected content was inert TODO: text, so nothing broke — which is exactly why it went unnoticed for a week. Had it contained real instructions, the behavior change would have been undebuggable: every surface a user checks reports that no style is active, so the investigation goes to the model, the prompts, or the project config. Never to /config, because /config says default.
The failure mode is misattribution, and it is silent by construction.
It is also not opt-in in any meaningful sense. The scaffolder ships the flag enabled, so the affected population is "anyone who ran the plugin scaffolder and left the directory in ~/.claude/skills/" — including plugins that were never finished, never enabled, and never intended to be active.
Reproduction
- From
~/.claude/skills/, run the built-in scaffolder:
```
claude plugin init repro-style --with skills agents hooks output-style
output-styles/repro-style.md
Note the generated already contains force-for-plugin: true and a TODO: body — no edit is required to reproduce the injection. To make it observable, replace the body with a canary:``yaml
---
name: repro-style
description: repro
force-for-plugin: true
keep-coding-instructions: true
---
DISTINCTIVE-CANARY-STRING-12345
```
- Confirm no
outputStylekey exists in any settings file. - Start a session. Ask Claude to quote its
# Output Style:section verbatim → the canary string is present. - Run
/config→ Output style: default. - Add a second such directory with a different canary; restart. The active style changes to the other plugin's, with no user action.
Three separable defects
force-for-plugin: trueis a scaffold default, in a file whose body is an unfilledTODO:. An unfinished plugin should not modify the system prompt of every session. This should default tofalse, or the scaffold should omit the key.
/configreportsdefaultwhile a forced style is active. Whatever the intended semantics offorce-for-plugin, the surface that reports the active output style must report it — otherwise the state is undiscoverable.
- Two plugins can both declare
force-for-plugin: true, and one silently wins. The arbitration is undocumented, and the outcome changes as directories are added or removed.
Defect 2 is the serious one and stands on its own even if the others are intended behavior.
Suggested direction
- Default
force-for-plugintofalsein the scaffolder (or omit it entirely). - Make
/configdisplay the effective output style and its source, e.g.slide-bundle (forced by plugin), rather thandefault. - Document the precedence when multiple plugins force a style — or make it an error rather than a silent pick.
Related issues
- #89774 reports the complementary failure: in a marketplace-installed plugin,
force-for-plugin: trueis ignored and the style never registers. This report is the same field in the opposite direction under skills-directory (in-place) loading — it applies unconditionally, with no way to see that it has. Together they suggestforce-for-pluginis handled inconsistently across the two loading mechanisms rather than being uniformly broken. - #85246 covers the
/configpicker listing only built-in styles. Distinct from defect 2 here: that is about a style not being selectable; this is about/configreportingdefaultas the active style while a different style is in the system prompt.
Environment
Claude Code 2.1.259, macOS (darwin), ~/.claude/skills/ skills-directory plugins loaded in place (not marketplace-installed, not in the plugin cache). Both plugins were scaffolded locally and left in place; no output style was ever selected.