Output style from a skills-directory plugin silently modifies every session's system prompt while /config reports "default"

Status Open
Reported on v2.1.259
Maintainer reply None cached
Activity 0 comments · opened Sep 3, 2026

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 outputStyle key required in any settings file,
  • and /config continuing 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

  1. From ~/.claude/skills/, run the built-in scaffolder:

``
claude plugin init repro-style --with skills agents hooks output-style
`
Note the generated
output-styles/repro-style.md 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
```

  1. Confirm no outputStyle key exists in any settings file.
  2. Start a session. Ask Claude to quote its # Output Style: section verbatim → the canary string is present.
  3. Run /config → Output style: default.
  4. 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

  1. force-for-plugin: true is a scaffold default, in a file whose body is an unfilled TODO:. An unfinished plugin should not modify the system prompt of every session. This should default to false, or the scaffold should omit the key.
  1. /config reports default while a forced style is active. Whatever the intended semantics of force-for-plugin, the surface that reports the active output style must report it — otherwise the state is undiscoverable.
  1. 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-plugin to false in the scaffolder (or omit it entirely).
  • Make /config display the effective output style and its source, e.g. slide-bundle (forced by plugin), rather than default.
  • 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: true is 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 suggest force-for-plugin is handled inconsistently across the two loading mechanisms rather than being uniformly broken.
  • #85246 covers the /config picker listing only built-in styles. Distinct from defect 2 here: that is about a style not being selectable; this is about /config reporting default as 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.

View original on GitHub ↗