Bug
claude-ops's plugins skill (SKILL.md) references ${user_config.install_new} expecting Claude Code to substitute the configured value (or fall back to "ask") before the model sees the rendered skill. When the option is unset, the literal string ${user_config.install_new} leaks through unrendered instead.
Root cause
In plugin.json, the install_new userConfig entry has no "default" key:
"install_new": {
"type": "string",
"title": "New-plugin install policy for the plugins skill's sync action",
"description": "Controls what `sync` does with catalog plugins that aren't installed yet. Valid values: \"ask\" (default — offer them in one batched multi-select prompt), \"all\" (install every one automatically), \"none\" (report only, never install). The manifest schema has no enum type, so this validates in prose, not JSON Schema; any other value is treated as \"ask\"."
}
Compare to the boolean options in the same manifest (e.g. api_error_audit_enabled), which all declare "default": true. Those presumably substitute correctly when unset; install_new has no such declared default, so there's nothing for Claude Code to substitute when the user hasn't configured it, and the raw template placeholder passes through into the skill body.
Impact
plugins sync's Step 4 (install new catalog plugins) cannot reliably branch on the configured policy — the skill has to detect the unrendered placeholder itself and fall back to ask, rather than the substitution mechanism doing it. SKILL.md's own text ("Any other value is invalid; treat it as ask") assumes a rendered but wrong value, not a never-rendered one — this is a distinct failure mode.
Suggested fix
Add "default": "ask" to the install_new userConfig entry in plugin.json, matching the documented default and the pattern used by every other option in the manifest.
Repro
- Do not set
install_new in any settings scope.
- Invoke
/claude-ops:plugins sync.
- Observe the skill's "Configured value:
${user_config.install_new}" line render with the literal placeholder instead of a substituted value.
Bug
claude-ops'spluginsskill (SKILL.md) references${user_config.install_new}expecting Claude Code to substitute the configured value (or fall back to "ask") before the model sees the rendered skill. When the option is unset, the literal string${user_config.install_new}leaks through unrendered instead.Root cause
In
plugin.json, theinstall_newuserConfig entry has no"default"key:Compare to the boolean options in the same manifest (e.g.
api_error_audit_enabled), which all declare"default": true. Those presumably substitute correctly when unset;install_newhas no such declared default, so there's nothing for Claude Code to substitute when the user hasn't configured it, and the raw template placeholder passes through into the skill body.Impact
plugins sync's Step 4 (install new catalog plugins) cannot reliably branch on the configured policy — the skill has to detect the unrendered placeholder itself and fall back toask, rather than the substitution mechanism doing it. SKILL.md's own text ("Any other value is invalid; treat it as ask") assumes a rendered but wrong value, not a never-rendered one — this is a distinct failure mode.Suggested fix
Add
"default": "ask"to theinstall_newuserConfig entry inplugin.json, matching the documented default and the pattern used by every other option in the manifest.Repro
install_newin any settings scope./claude-ops:plugins sync.${user_config.install_new}" line render with the literal placeholder instead of a substituted value.