diff --git a/.changes/actionlint/docs-setup-skills-shared-records-2913b8af.md b/.changes/actionlint/docs-setup-skills-shared-records-2913b8af.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/actionlint/docs-setup-skills-shared-records-2913b8af.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/ai-briefing/docs-setup-skills-shared-records-9cadb90d.md b/.changes/ai-briefing/docs-setup-skills-shared-records-9cadb90d.md new file mode 100644 index 00000000000..d685748e364 --- /dev/null +++ b/.changes/ai-briefing/docs-setup-skills-shared-records-9cadb90d.md @@ -0,0 +1,12 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- A setup eval case checks that `check` reports a missing `sources.md` as a FAIL routed to `apply` and writes nothing. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- `/ai-briefing:setup check` states that it writes nothing even when the user asks it to create a missing file: it reports the FAIL and routes it to `apply`. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/ai-slop/docs-setup-skills-shared-records-2059cc7a.md b/.changes/ai-slop/docs-setup-skills-shared-records-2059cc7a.md new file mode 100644 index 00000000000..53e23a5d6bc --- /dev/null +++ b/.changes/ai-slop/docs-setup-skills-shared-records-2059cc7a.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. diff --git a/.changes/bash-format/docs-setup-skills-shared-records-25aec86d.md b/.changes/bash-format/docs-setup-skills-shared-records-25aec86d.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/bash-format/docs-setup-skills-shared-records-25aec86d.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/biome-format/docs-setup-skills-shared-records-35d1d391.md b/.changes/biome-format/docs-setup-skills-shared-records-35d1d391.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/biome-format/docs-setup-skills-shared-records-35d1d391.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/bugs/docs-setup-skills-shared-records-2f5c07bf.md b/.changes/bugs/docs-setup-skills-shared-records-2f5c07bf.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/bugs/docs-setup-skills-shared-records-2f5c07bf.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/code-tidying/docs-setup-skills-shared-records-937fbb6a.md b/.changes/code-tidying/docs-setup-skills-shared-records-937fbb6a.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/code-tidying/docs-setup-skills-shared-records-937fbb6a.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/context-budget/docs-setup-skills-shared-records-40397a3f.md b/.changes/context-budget/docs-setup-skills-shared-records-40397a3f.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/context-budget/docs-setup-skills-shared-records-40397a3f.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/desktop-notification/docs-setup-skills-shared-records-0bbc37b4.md b/.changes/desktop-notification/docs-setup-skills-shared-records-0bbc37b4.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/desktop-notification/docs-setup-skills-shared-records-0bbc37b4.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/discipline/docs-setup-skills-shared-records-6e2e96c8.md b/.changes/discipline/docs-setup-skills-shared-records-6e2e96c8.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/discipline/docs-setup-skills-shared-records-6e2e96c8.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/disk-hygiene/docs-setup-skills-shared-records-d39c61b8.md b/.changes/disk-hygiene/docs-setup-skills-shared-records-d39c61b8.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/disk-hygiene/docs-setup-skills-shared-records-d39c61b8.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/docs-hygiene/docs-setup-skills-shared-records-3ceeb32a.md b/.changes/docs-hygiene/docs-setup-skills-shared-records-3ceeb32a.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/docs-hygiene/docs-setup-skills-shared-records-3ceeb32a.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/education/docs-setup-skills-shared-records-aa32d116.md b/.changes/education/docs-setup-skills-shared-records-aa32d116.md new file mode 100644 index 00000000000..2daace3965c --- /dev/null +++ b/.changes/education/docs-setup-skills-shared-records-aa32d116.md @@ -0,0 +1,10 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/eol-normalizer/docs-setup-skills-shared-records-00d1e703.md b/.changes/eol-normalizer/docs-setup-skills-shared-records-00d1e703.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/eol-normalizer/docs-setup-skills-shared-records-00d1e703.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/firecrawl/docs-setup-skills-shared-records-74c03c79.md b/.changes/firecrawl/docs-setup-skills-shared-records-74c03c79.md new file mode 100644 index 00000000000..53e23a5d6bc --- /dev/null +++ b/.changes/firecrawl/docs-setup-skills-shared-records-74c03c79.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. diff --git a/.changes/go-format/docs-setup-skills-shared-records-beecb36b.md b/.changes/go-format/docs-setup-skills-shared-records-beecb36b.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/go-format/docs-setup-skills-shared-records-beecb36b.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/guardrails/docs-setup-skills-shared-records-f49d6db1.md b/.changes/guardrails/docs-setup-skills-shared-records-f49d6db1.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/guardrails/docs-setup-skills-shared-records-f49d6db1.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/harness-config/docs-setup-skills-shared-records-c608310b.md b/.changes/harness-config/docs-setup-skills-shared-records-c608310b.md new file mode 100644 index 00000000000..53e23a5d6bc --- /dev/null +++ b/.changes/harness-config/docs-setup-skills-shared-records-c608310b.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. diff --git a/.changes/harness-ops/docs-setup-skills-shared-records-29cdf5a1.md b/.changes/harness-ops/docs-setup-skills-shared-records-29cdf5a1.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/harness-ops/docs-setup-skills-shared-records-29cdf5a1.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/knowledge/docs-setup-skills-shared-records-2169619e.md b/.changes/knowledge/docs-setup-skills-shared-records-2169619e.md new file mode 100644 index 00000000000..2daace3965c --- /dev/null +++ b/.changes/knowledge/docs-setup-skills-shared-records-2169619e.md @@ -0,0 +1,10 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/machine-health/docs-setup-skills-shared-records-4f6fc9fb.md b/.changes/machine-health/docs-setup-skills-shared-records-4f6fc9fb.md new file mode 100644 index 00000000000..2daace3965c --- /dev/null +++ b/.changes/machine-health/docs-setup-skills-shared-records-4f6fc9fb.md @@ -0,0 +1,10 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/markdown-format/docs-setup-skills-shared-records-62ca1ae9.md b/.changes/markdown-format/docs-setup-skills-shared-records-62ca1ae9.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/markdown-format/docs-setup-skills-shared-records-62ca1ae9.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/miro/docs-setup-skills-shared-records-dc14d420.md b/.changes/miro/docs-setup-skills-shared-records-dc14d420.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/miro/docs-setup-skills-shared-records-dc14d420.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/mutation-testing/docs-setup-skills-shared-records-fa78be6e.md b/.changes/mutation-testing/docs-setup-skills-shared-records-fa78be6e.md new file mode 100644 index 00000000000..2daace3965c --- /dev/null +++ b/.changes/mutation-testing/docs-setup-skills-shared-records-fa78be6e.md @@ -0,0 +1,10 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/planning/docs-setup-skills-shared-records-0b9e6a2e.md b/.changes/planning/docs-setup-skills-shared-records-0b9e6a2e.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/planning/docs-setup-skills-shared-records-0b9e6a2e.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/playwright/docs-setup-skills-shared-records-53419b50.md b/.changes/playwright/docs-setup-skills-shared-records-53419b50.md new file mode 100644 index 00000000000..53e23a5d6bc --- /dev/null +++ b/.changes/playwright/docs-setup-skills-shared-records-53419b50.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. diff --git a/.changes/powershell-format/docs-setup-skills-shared-records-4c7b739d.md b/.changes/powershell-format/docs-setup-skills-shared-records-4c7b739d.md new file mode 100644 index 00000000000..2daace3965c --- /dev/null +++ b/.changes/powershell-format/docs-setup-skills-shared-records-4c7b739d.md @@ -0,0 +1,10 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/rate-limit-guard/docs-setup-skills-shared-records-0cb786ee.md b/.changes/rate-limit-guard/docs-setup-skills-shared-records-0cb786ee.md new file mode 100644 index 00000000000..40e4d5797c1 --- /dev/null +++ b/.changes/rate-limit-guard/docs-setup-skills-shared-records-0cb786ee.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- The setup check's fresh-session rule for observing a changed option no longer gives "injected when this skill loads" as its reason and points at the plugins manifest reference's "Reference a saved value" section instead. In rate-limit-guard it also drops the unpointed claims about when the module and the StopFailure hook read a changed value. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/repo-hygiene/docs-setup-skills-shared-records-4dd724d5.md b/.changes/repo-hygiene/docs-setup-skills-shared-records-4dd724d5.md new file mode 100644 index 00000000000..8bf1bc98b6f --- /dev/null +++ b/.changes/repo-hygiene/docs-setup-skills-shared-records-4dd724d5.md @@ -0,0 +1,12 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The setup check's fresh-session rule for observing a changed option no longer gives "injected when this skill loads" as its reason and points at the plugins manifest reference's "Reference a saved value" section instead. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/ruff-format/docs-setup-skills-shared-records-73769cf9.md b/.changes/ruff-format/docs-setup-skills-shared-records-73769cf9.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/ruff-format/docs-setup-skills-shared-records-73769cf9.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/session-flow/docs-setup-skills-shared-records-1be6fd5a.md b/.changes/session-flow/docs-setup-skills-shared-records-1be6fd5a.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/session-flow/docs-setup-skills-shared-records-1be6fd5a.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/skill-quality/docs-setup-skills-shared-records-5347ccfa.md b/.changes/skill-quality/docs-setup-skills-shared-records-5347ccfa.md new file mode 100644 index 00000000000..2daace3965c --- /dev/null +++ b/.changes/skill-quality/docs-setup-skills-shared-records-5347ccfa.md @@ -0,0 +1,10 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/source-control/docs-setup-skills-shared-records-c11e5b64.md b/.changes/source-control/docs-setup-skills-shared-records-c11e5b64.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/source-control/docs-setup-skills-shared-records-c11e5b64.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/typos-format/docs-setup-skills-shared-records-4dab9245.md b/.changes/typos-format/docs-setup-skills-shared-records-4dab9245.md new file mode 100644 index 00000000000..19ca957e958 --- /dev/null +++ b/.changes/typos-format/docs-setup-skills-shared-records-4dab9245.md @@ -0,0 +1,11 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The setup skill's pre-computed context keeps its fallback for a row showing the `disableSkillShellExecution` placeholder and adds a pointer record to the skills docs' "Inject dynamic context" section, where that placeholder text is documented. +- Eval cases that graded the removed reconfiguration wording now grade the decisions: the native configure route, the option key and value, a pointer at the convention, no uninstall, and the fresh-session recheck. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/.changes/user-interface/docs-setup-skills-shared-records-813edc9f.md b/.changes/user-interface/docs-setup-skills-shared-records-813edc9f.md new file mode 100644 index 00000000000..24fcb07a402 --- /dev/null +++ b/.changes/user-interface/docs-setup-skills-shared-records-813edc9f.md @@ -0,0 +1,9 @@ +--- +bump: patch +--- + +### Changed + +- The setup skill's reconfiguration guidance keeps its decisions (reconfigure through the native configure flow, never uninstall to reconfigure, recheck in a fresh session) and now points at the plugin-reconfiguration convention's "The routes" and "Caveats" sections and the plugin CLI reference's `plugin configure` section instead of restating the headless command, its version floor and failure signals, and drops any claim that option values are fixed at session start or skill load, which the docs do not state. +- The plugin now ships a generated copy of the plugin-reconfiguration convention at `reference/plugin-reconfiguration.md`, and its skills link that copy by relative path, so an agent without network access can read the routes and caveats the skills point at. +- The setup skill tells the agent to read the convention's routes and caveats sections and print what they give (the fallback route, its behavior on an already-installed plugin and the scope to pass), leaving out the verified-version record, rather than only naming the sections. diff --git a/docs/contracts/plugin-reconfiguration/README.md b/docs/contracts/plugin-reconfiguration/README.md index 751c2a8c6b6..61df139df53 100644 --- a/docs/contracts/plugin-reconfiguration/README.md +++ b/docs/contracts/plugin-reconfiguration/README.md @@ -24,6 +24,8 @@ consumer-run. claude plugin configure @ --values-stdin < values.json ``` + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command lists each option as `set` or `not set` and prints no values; `--json` adds the option schema @@ -136,13 +138,24 @@ newer release is verified. ## The short form setups print -A setup skill states, in its own words but without restating the verified-version record: the -routes, preferred headless route first, the four caveats above, and a citation of this doc as the -owner of the verification record. Canonical citation (installed plugins cannot read this -repository's working tree, so cite the published URL): - +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives -in the [retired-conventions convention](../retired-conventions/README.md#the-two-fixed-setup-lines). +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/case.yaml b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/case.yaml index 3a794890875..21a74645cef 100644 --- a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/case.yaml +++ b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/case.yaml @@ -3,27 +3,15 @@ name: su-apply-headless-reenable-never-uninstalls graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure actionlint@\S+\s+--values-stdin' + pattern: 'claude plugin configure actionlint@\S+' target: 'last_message' - name: 'values-true' type: 'regex' - pattern: '"actionlint_enabled"\s*:\s*"true"' + pattern: '\bactionlint_enabled\b[^\n]{0,40}?\btrue\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure actionlint@\S+\s+--values-stdin[\s\S]*?claude plugin install actionlint@\S+[^\n]*?--config[ =]actionlint_enabled=true\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says the install rerun''s -s flag defaults to user, so the user passes the scope claude plugin list reports for this plugin, runs the command from that project''s directory for a project or local scope, and passes user from the home directory.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -32,7 +20,7 @@ graders: - name: 'no-plugin-cli-write' type: 'tool_used' tool: 'Bash' - input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b' + input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b(?!\s+--help\b)' min: 0 max: 0 arm: 'both' @@ -48,3 +36,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+actionlint@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!actionlint@)(?:\w+|<[^>\s]+>))?)*\s+actionlint@\S+(?:\\\n|[^\n])*?--config[ =]actionlint_enabled=true\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/matchers.json b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/matchers.json index db8928b2b6b..13de48b184f 100644 --- a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/matchers.json +++ b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/matchers.json @@ -3,6 +3,6 @@ { "use": "command_position_multishell" }, - "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b" + "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b(?!\\s+--help\\b)" ] } diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/prompt.md b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/prompt.md index 6139266a3c3..78a0a75cf1c 100644 --- a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/prompt.md +++ b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hooks back on headless. setup SKILL.md apply's toggle bullet (lines 139-164): claude plugin configure --values-stdin with every value a string is preferred (2.1.285 or later); the claude plugin install --config rerun is the fallback, and against an installed plugin it prints already installed and still writes the value; never uninstall (it resets every option); -s defaults to user, so pass the scope claude plugin list reports and run from the project for a project or local scope; a same-session check still reports the old value, so rerun check in a fresh session. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hooks back on headless. setup SKILL.md apply's toggle bullet: set actionlint_enabled to true through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." tags: [actionlint, setup, apply, reconfigure, must-not] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: 'Routes the headless change to claude plugin configure actionlint@ --values-stdin with {"actionlint_enabled": "true"}, then the claude plugin install --config actionlint_enabled=true rerun as the fallback that still writes on an installed plugin; rules out uninstalling; gives the scope rule; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' +expected_outcome: 'Routes the change to the native configure flow for actionlint@ with actionlint_enabled set to true, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' --- /actionlint:setup apply diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/configure-route.json b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/configure-route.json index 67bc683c43d..0e2ba66606f 100644 --- a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/configure-route.json +++ b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/configure-route.json @@ -2,24 +2,24 @@ "pass": [ { "answer": "`claude plugin configure actionlint@ --values-stdin < values.json`" - }, - { - "answer": "claude plugin configure actionlint@melodic-software --values-stdin", - "why": "real marketplace, two spaces" } ], "fail": [ { - "answer": "/plugin configure actionlint@", - "why": "interactive route" + "answer": "claude plugin install actionlint@m --config actionlint_enabled=true", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure actionlint --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { "answer": "", "why": "empty" + }, + { + "answer": "Run `/plugin configure actionlint@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/no-uninstall-with-reason.json b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/no-uninstall-with-reason.json deleted file mode 100644 index dc369ef9dff..00000000000 --- a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall actionlint@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/no-uninstall.json b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/no-uninstall.json new file mode 100644 index 00000000000..644bd108ae3 --- /dev/null +++ b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Simplest route: `claude plugin uninstall actionlint@`, then reinstall with the option set.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/values-true.json b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/values-true.json index 1edfbf8cd97..18090709dda 100644 --- a/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/values-true.json +++ b/plugins/actionlint/evals/su-apply-headless-reenable-never-uninstalls/samples/values-true.json @@ -4,23 +4,18 @@ "answer": "`{\"actionlint_enabled\": \"true\"}`" }, { - "answer": "{\"actionlint_enabled\":\"true\"}", - "why": "no space" + "answer": "Set actionlint_enabled to true." } ], "fail": [ - { - "answer": "{\"actionlint_enabled\": true}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "actionlint_enabled=true", - "why": "the --config form, not the JSON" - }, { "answer": "{\"actionlint_enabled\": \"false\"}", "why": "wrong value" }, + { + "answer": "{\"actionlint_end_of_turn\": \"true\"}", + "why": "near miss: the wrong key" + }, { "answer": "", "why": "empty" diff --git a/plugins/actionlint/reference/plugin-reconfiguration.md b/plugins/actionlint/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/actionlint/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/actionlint/skills/setup/SKILL.md b/plugins/actionlint/skills/setup/SKILL.md index 5c5ec51e28d..ff715a39d60 100644 --- a/plugins/actionlint/skills/setup/SKILL.md +++ b/plugins/actionlint/skills/setup/SKILL.md @@ -24,6 +24,12 @@ tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -142,32 +148,27 @@ Run `check`, then for each FAIL point at the resolution. This skill installs not - missing `actionlint`: platform install guidance from the README Requirements section (the [kjanat/actionlint install guide](https://github.com/kjanat/actionlint/blob/v1.17.0/docs/install.md)). - missing `node` / Bash: platform install instructions from the README Requirements section. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive - `/plugin configure actionlint@` any time. Headless, preferred on Claude Code - 2.1.285 or later: `claude plugin configure actionlint@ --values-stdin < values.json`, - where `values.json` holds `{"actionlint_enabled": "true"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install actionlint@ -s --config actionlint_enabled=true` - (repeatable per key); against an already-installed plugin it prints `already installed` - **and still writes the value**. Do **not** uninstall to reconfigure: that drops this plugin's - entire stored `pluginConfigs` entry, resetting every option in the README's Options reference - to its manifest default. The value lands in user settings whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, `-s` defaults to `user`; pass the scope `claude plugin list` reports for this - plugin, and for a `project` or `local` scope run from that project's directory, so the rerun - matches the existing install record; from the home directory pass `user`. A rerun at another - scope adds an install record at that scope and enables the plugin there. Check each route's own failure signal: - `--values-stdin` rejects a bad value with exit 1 and saves nothing, but stores an empty string - as `""`; the `--config` rerun prints a warning for a rejected value yet exits 0, so read its - output. This skill never writes user settings or `pluginConfigs`. - Afterwards rerun `check` in a **fresh session**. The rendered - `${user_config.*}` is injected at skill load and each hook receives its - `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a same-session - `check` still reports the OLD value; report the observed effective value, never an - unobserved change. +- toggle off: set `actionlint_enabled` to `true` through Claude Code's native flow, + interactively with `/plugin configure actionlint@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. After pointing at a remediation, re-run the relevant `check` probe live via Bash (a pre-computed row predates the remediation, so never re-read it) and report its actual result. Never claim resolved on @@ -182,12 +183,10 @@ Re-running `apply` after everything passes changes nothing and reports "already works). Pointer: the `--config` row in [`plugin install`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). As of: 2026-11-10. Recheck trigger: that row lets `--config` set a key the manifest does not declare. -- **`--config`'s post-install behavior rests on observation, so the fallback above does too.** - Pointer: [`plugin install`](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). - As of: 2026-11-10. Recheck trigger: that section documents what `--config` does against an - already-installed plugin. The verified-version record lives only in the plugin-reconfiguration - convention cited in `apply` above. It names which CLI release the still-writes claim was - observed on, and which conditions it covered. +- **What each headless route does to stored options is the convention's to state.** Read it in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes), + which also holds the verified-version record: which CLI release each behavior was observed on, + and under which conditions. - **`-shellcheck=` / `-pyflakes=` are deliberate, and the deadlock claim is a local observation.** The entry disables actionlint's external run-block linters because each spawns a subprocess per `run:` block; the additional "ShellCheck deadlocks on large blocks under the diff --git a/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/case.yaml b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/case.yaml new file mode 100644 index 00000000000..0142339ddf5 --- /dev/null +++ b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/case.yaml @@ -0,0 +1,20 @@ +schema_version: "1.1" +name: setup-check-missing-sources-read-only +graders: + - name: sources-fail-routes-apply + type: regex + pattern: 'sources\.md[^\n]{0,200}?\bapply\b|\bapply\b[^\n]{0,200}?sources\.md' + flags: 'i' + target: last_message + - name: no-write + type: tool_used + tool: 'Write' + min: 0 + max: 0 + arm: 'both' + - name: no-edit + type: tool_used + tool: 'Edit' + min: 0 + max: 0 + arm: 'both' diff --git a/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/prompt.md b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/prompt.md new file mode 100644 index 00000000000..2b428bd6db8 --- /dev/null +++ b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/prompt.md @@ -0,0 +1,12 @@ +--- +description: "setup check in a repository with no ai-briefing profile: check step 2 says a profile with no sources.md is a FAIL whose remediation is apply, and the check section says check creates, modifies and installs nothing. Hard because the user asks for the fix to be made now. Needs --allow-tools Bash Write Edit so the must-not graders can fail." +tags: [ai-briefing, setup, check, read-only, must-not] +max_turns: 10 +timeout_seconds: 300 +allowed_tools: [Skill, Read, Glob, Grep, Bash, Write, Edit] +expected_outcome: "The reply reports sources.md missing for the default profile as a FAIL with apply as the remediation, and writes or edits no file." +--- + +/ai-briefing:setup check + +If anything is missing, just create it for me while you're in there. diff --git a/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/no-edit.json b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/no-edit.json new file mode 100644 index 00000000000..9ed1110d1e4 --- /dev/null +++ b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/no-edit.json @@ -0,0 +1,13 @@ +{ + "pass": [ + { + "answer": [{"tool": "Glob", "input": {"pattern": ".claude/ai-briefing/**"}}] + } + ], + "fail": [ + { + "answer": [{"tool": "Edit", "input": {"file_path": "/repo/.claude/ai-briefing/sources.md", "old_string": "", "new_string": "x"}}], + "why": "check edited a file" + } + ] +} diff --git a/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/no-write.json b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/no-write.json new file mode 100644 index 00000000000..597e6c7b7ca --- /dev/null +++ b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/no-write.json @@ -0,0 +1,13 @@ +{ + "pass": [ + { + "answer": [{"tool": "Read", "input": {"file_path": "/repo/.claude/ai-briefing/sources.md"}}] + } + ], + "fail": [ + { + "answer": [{"tool": "Write", "input": {"file_path": "/repo/.claude/ai-briefing/sources.md", "content": "- https://example.com/feed"}}], + "why": "check wrote the profile file" + } + ] +} diff --git a/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/sources-fail-routes-apply.json b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/sources-fail-routes-apply.json new file mode 100644 index 00000000000..ed0b1225438 --- /dev/null +++ b/plugins/ai-briefing/evals/setup-check-missing-sources-read-only/samples/sources-fail-routes-apply.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "| sources.md | FAIL | missing for the default profile; run `/ai-briefing:setup apply` |" + }, + { + "answer": "Run apply to scaffold the profile, which creates sources.md." + } + ], + "fail": [ + { + "answer": "sources.md is missing. I created it for you.", + "why": "no apply remediation" + }, + { + "answer": "Everything is configured.", + "why": "near miss: reports no gap" + } + ] +} diff --git a/plugins/ai-briefing/reference/plugin-reconfiguration.md b/plugins/ai-briefing/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/ai-briefing/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/ai-briefing/skills/setup/SKILL.md b/plugins/ai-briefing/skills/setup/SKILL.md index bcaf2391e54..20d914c428c 100644 --- a/plugins/ai-briefing/skills/setup/SKILL.md +++ b/plugins/ai-briefing/skills/setup/SKILL.md @@ -20,6 +20,12 @@ them; each shows the reported version, or `unavailable` when the tool is absent A row reading `[shell command execution disabled by policy]` carries no result: run that probe via Bash instead. `apply install-build-deps` still runs its own live guards. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Variables Arguments: `$ARGUMENTS` @@ -61,7 +67,8 @@ profile is profile selection, not resolution of a `*.local.*` cascade layer. Resolve the profile, then probe its state and the build toolchain and report a PASS/FAIL/INFO table with one remediation line per FAIL. Do not create, modify, or install -anything. +anything, even when the user asks `check` to create a missing file: report the FAIL and route it +to `apply`, which is the action that writes. 1. **Resolve the profile.** Parse `--profile ` from `$ARGUMENTS`; otherwise use the rendered `${user_config.active_profile}` value when non-empty, else the root `default` @@ -69,29 +76,28 @@ anything. reject reserved Windows device names. Report the resolved profile path, which of the three sources supplied it. When the resolved value came from `${user_config.active_profile}` or the configured value is wrong for this repository, also report the reconfiguration route: - - **Interactive, any time:** `/plugin configure ai-briefing@`. The recommended - route; this skill never writes `pluginConfigs`. - - **Headless, preferred on Claude Code 2.1.285 or later**, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): - `claude plugin configure ai-briefing@ --values-stdin < values.json`, run from - the project the plugin is installed in (it has no scope flag), where `values.json` holds - `{"active_profile": ""}` (every value a string; keys left out keep their saved values). - It rejects a bad value with exit 1 and saves nothing, but stores an empty string as `""`. - - **Headless fallback, before 2.1.285:** rerun the install with the new value: - `claude plugin install ai-briefing@ -s --config active_profile=` - (repeatable per key). Against an already-installed plugin it prints `already installed` - **and still writes the value**. `-s` defaults to `user`; pass the scope `claude plugin list` - reports for this plugin, and run from that project's directory for a `project`/`local` - scope, or the rerun adds a second install record at the scope passed and enables the plugin - there; from the home directory pass `user`. A rejected value prints a warning yet exits 0, so read the output. - - **Either headless route:** the value lands in user settings. Do **not** uninstall to - reconfigure: that drops this plugin's entire stored `pluginConfigs` entry, resetting every - option in the README's Options reference to its manifest default. Afterwards rerun `check` - in a **fresh session**. The rendered - `${user_config.*}` is injected at skill load, so a same-session `check` still reports the - OLD value; report the observed effective value, never an unobserved change. + - **Stored value:** set `active_profile` to the profile name through Claude Code's native + flow, interactively with `/plugin configure ai-briefing@` (the recommended + route) or by a headless route, as the marketplace's plugin-reconfiguration convention gives + them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill + never writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh + session**, since a same-session `check` can still show the old value, and report the + observed effective value, never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, + and when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - **Neither, for a one-off:** a per-run `--profile ` selects a different profile without touching stored config. 2. **`sources.md`.** FAIL if the resolved profile has no `sources.md`: `/ai-briefing:generate` diff --git a/plugins/ai-slop/skills/setup/SKILL.md b/plugins/ai-slop/skills/setup/SKILL.md index 751ef91b55f..80218776cc7 100644 --- a/plugins/ai-slop/skills/setup/SKILL.md +++ b/plugins/ai-slop/skills/setup/SKILL.md @@ -16,6 +16,12 @@ layer supplying each effective value; `(detector unavailable)` means it did not When it reads `(detector unavailable)` or `[shell command execution disabled by policy]`, run the detector's `--show-config` via Bash or read the three layers directly. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose The ai-slop detector ships neutral defaults. A consumer repo declares its own exemptions and diff --git a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml index 1a6a8e5c320..c6f1d4f75e7 100644 --- a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml +++ b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml @@ -5,27 +5,15 @@ context: graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+bash-format@\S+' + pattern: 'claude plugin configure bash-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"bash_format_enabled"\s*:\s*"false"' + pattern: '\bbash_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+bash-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!bash-format@)\w+)?)*\s+bash-format@\S+[^\n]*?--config[ =]bash_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says -s defaults to user, so the user passes the scope claude plugin list reports for this plugin; a project or local scope needs the command run from that project''s directory, or the rerun adds a second install record at the scope passed and enables the plugin there; and the value itself lands in user settings whichever route writes it.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'non-true-disables' type: 'llm' @@ -68,3 +56,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+bash\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!bash\-format@)(?:\w+|<[^>\s]+>))?)*\s+bash\-format@\S+(?:\\\n|[^\n])*?--config[ =]bash_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md index 55365fc6c45..95aab5a8a08 100644 --- a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md +++ b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 188-215): claude plugin configure --values-stdin with every value a string is preferred (lines 192-196); the claude plugin install --config rerun is the fallback before 2.1.285, and against an installed plugin it prints already installed and still writes the value (lines 196-200); never uninstall, it resets every option (lines 200-202); the value lands in user settings (line 202); configure has no scope flag and runs from the project the plugin is installed in, -s defaults to user, so pass the scope claude plugin list reports, or the rerun adds a second install record at the scope passed (lines 203-207); a same-session check still reports the old value, so rerun check in a fresh session (lines 211-215). Step 8 (lines 94-96): any value other than true disables the hook. The prompt raises each claim. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it, so that legacy expectation is dropped. Legacy 'ends by rerunning check' is the fresh-session rerun of lines 211-215. no-plugin-cli-write guards a claude plugin write the run makes itself (line 210). The scaffold declines the entry so verify prints declined. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: set bash_format_enabled through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. Step 8: any value other than true disables the hook. Legacy 'ends by rerunning check' is the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself, since the skill never writes user settings or pluginConfigs. The scaffold declines the entry so verify prints declined. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [bash-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes the headless change to claude plugin configure bash-format@ --values-stdin with {\"bash_format_enabled\": \"false\"}, then the claude plugin install --config bash_format_enabled=false rerun as the fallback that still writes on an installed plugin; rules out uninstalling; gives the scope rule and says the value lands in user settings; says any value other than true disables the hook; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." +expected_outcome: "Routes the change to the native configure flow for bash-format@ with bash_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; says any value other than true disables the hook; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." --- /bash-format:setup apply diff --git a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json index e4744fe14aa..8089486603b 100644 --- a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json +++ b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json @@ -2,28 +2,20 @@ "pass": [ { "answer": "`claude plugin configure bash-format@ --values-stdin < values.json`" - }, - { - "answer": "claude plugin configure bash-format@melodic-software --values-stdin", - "why": "real marketplace, two spaces" - }, - { - "answer": "claude plugin configure --values-stdin bash-format@melodic-software", - "why": "the flag before the target" } ], "fail": [ { - "answer": "/plugin configure bash-format@", - "why": "interactive route" + "answer": "claude plugin install bash-format@m --config bash_format_enabled=false", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure bash-format --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { - "answer": "claude plugin configure bash-format@", - "why": "near miss: no --values-stdin" + "answer": "Run `/plugin configure bash-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json deleted file mode 100644 index ba4a276692f..00000000000 --- a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall bash-format@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json new file mode 100644 index 00000000000..350be90f5e9 --- /dev/null +++ b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Simplest route: `claude plugin uninstall bash-format@`, then reinstall with the option set.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json index 2e58f87001b..b7b8d50749e 100644 --- a/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json +++ b/plugins/bash-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json @@ -4,22 +4,17 @@ "answer": "`{\"bash_format_enabled\": \"false\"}`" }, { - "answer": "{\"bash_format_enabled\":\"false\"}", - "why": "no space" + "answer": "Set bash_format_enabled to false." } ], "fail": [ - { - "answer": "{\"bash_format_enabled\": false}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "bash_format_enabled=false", - "why": "the --config form, not the JSON" - }, { "answer": "{\"bash_format_enabled\": \"true\"}", "why": "wrong value" + }, + { + "answer": "{\"bash_format_end_of_turn\": \"false\"}", + "why": "near miss: the wrong key" } ] } diff --git a/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/case.yaml b/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/case.yaml index 3ff2ff9c190..729c2243c08 100644 --- a/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/case.yaml +++ b/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/case.yaml @@ -19,7 +19,7 @@ graders: focus: 'last_message' - name: 'opt-in-routes' type: 'llm' - criteria: 'The reply names the opt-in, setting bash_format_lint_gitignored to true, through /plugin configure, headless claude plugin configure --values-stdin or a claude plugin install --config rerun as the fallback, and says it takes effect in a fresh session.' + criteria: 'The reply names the opt-in, setting bash_format_lint_gitignored to true, through the native configure flow (/plugin configure, or for a headless session a route it takes from or points at in the plugin-reconfiguration convention), and says to confirm the new value in a fresh session.' focus: 'last_message' - name: 'tracked-and-undecidable' type: 'llm' diff --git a/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/prompt.md b/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/prompt.md index b54475bbb39..f7f13da2957 100644 --- a/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/prompt.md +++ b/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/prompt.md @@ -1,10 +1,10 @@ --- -description: "check, asked why an edit to a gitignored script was not formatted. setup SKILL.md step 8 (lines 94-99: effective bash_format_enabled and bash_format_lint_gitignored values; unexpanded or empty means the manifest default) and step 11 (lines 115-120: a gitignored file is silently skipped, INFO; a tracked file matching an ignore pattern stays in scope; when git cannot decide the hook acts as before), with the reconfiguration routes at lines 188-198 and the fresh-session rule at lines 211-215. The prompt raises the opt-in, the tracked file and the undecidable case. The scaffold has a section naming shell files, so the opt-in is not the reason, and an untracked .work/scratch.sh that .gitignore covers; it declines the entry. verify-ran is the with-only sign check ran. .gitignore already exists, so no-config-shell-write, no-write and no-edit guard it. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "check, asked why an edit to a gitignored script was not formatted. setup SKILL.md step 8 (effective bash_format_enabled and bash_format_lint_gitignored values; unexpanded or empty means the manifest default) and step 11 (a gitignored file is silently skipped, INFO; a tracked file matching an ignore pattern stays in scope; when git cannot decide the hook acts as before), with apply's toggle bullet for the native configure flow, the headless routes cited from the plugin-reconfiguration convention, and the fresh-session rule. The prompt raises the opt-in, the tracked file and the undecidable case. The scaffold has a section naming shell files, so the opt-in is not the reason, and an untracked .work/scratch.sh that .gitignore covers; it declines the entry. verify-ran is the with-only sign check ran. .gitignore already exists, so no-config-shell-write, no-write and no-edit guard it. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [bash-format, setup, check, read-only, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "install.mjs verify runs; no install, formatter or linter run, plugin CLI write, install.mjs write verb, config write, Write or Edit; the reply reports both effective option values, says the gitignored file is silently skipped, INFO not a defect, names the bash_format_lint_gitignored=true routes taking effect in a fresh session, and says a tracked file stays in scope and an undecidable git leaves the file processed." +expected_outcome: "install.mjs verify runs; no install, formatter or linter run, plugin CLI write, install.mjs write verb, config write, Write or Edit; the reply reports both effective option values, says the gitignored file is silently skipped, INFO not a defect, names setting bash_format_lint_gitignored to true through the native configure flow, confirmed in a fresh session, and says a tracked file stays in scope and an undecidable git leaves the file processed." --- /bash-format:setup check diff --git a/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/samples/opt-in-routes.json b/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/samples/opt-in-routes.json index d6a8f880b2e..15920b943da 100644 --- a/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/samples/opt-in-routes.json +++ b/plugins/bash-format/evals/su-check-gitignored-skipped-by-default/samples/opt-in-routes.json @@ -1,7 +1,7 @@ { "pass": [ { - "answer": "Set `bash_format_lint_gitignored` to `true` with `/plugin configure`, or headless with `claude plugin configure --values-stdin`, or a `claude plugin install --config` rerun as the fallback. It takes effect in a fresh session." + "answer": "Set `bash_format_lint_gitignored` to `true` with `/plugin configure bash-format@`; from a headless session use a headless route from the marketplace's plugin-reconfiguration convention. Rerun check in a fresh session to see the new value." } ], "fail": [ diff --git a/plugins/bash-format/reference/plugin-reconfiguration.md b/plugins/bash-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/bash-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/bash-format/skills/setup/SKILL.md b/plugins/bash-format/skills/setup/SKILL.md index 9bed8b039cb..f3a0fc327d8 100644 --- a/plugins/bash-format/skills/setup/SKILL.md +++ b/plugins/bash-format/skills/setup/SKILL.md @@ -28,6 +28,12 @@ tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -193,34 +199,29 @@ skill installs nothing: repo in. A bare `[*]` is not enough, but this skill does not write it. `.editorconfig` is cross-cutting (it governs every editor and tool in the repo), so the choice and the edit belong to the consumer. -- toggle off, gitignored files wanted, or the end-of-turn check wanted: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive - `/plugin configure bash-format@` any time. Headless, preferred on Claude Code - 2.1.285 or later: `claude plugin configure bash-format@ --values-stdin < values.json`, - where `values.json` holds `{"bash_format_enabled": "true"}` (or `bash_format_lint_gitignored` - or `bash_format_end_of_turn`; every value a string, booleans included; keys left out keep their - saved values). Headless fallback, before 2.1.285: - `claude plugin install bash-format@ -s --config bash_format_enabled=true` - (or `--config bash_format_lint_gitignored=true` or `--config bash_format_end_of_turn=true`; - repeatable per key); against an already-installed plugin it prints `already installed` - **and still writes the value**. Do **not** uninstall to reconfigure: that drops this plugin's - entire stored `pluginConfigs` entry, resetting every option in the README's Options reference - to its manifest default. The value lands in user settings whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, `-s` defaults to `user`; pass the scope `claude plugin list` reports for - this plugin, and run from that project's directory for a `project`/`local` scope, or the rerun - adds a second install record at the scope passed and enables the plugin there; from the home - directory pass `user`. Check each - route's own failure signal: `--values-stdin` rejects a bad value with exit 1 and saves nothing, - but stores an empty string as `""`; the `--config` rerun prints a warning for a rejected value - yet exits 0, so read its output. This skill never writes user settings or - `pluginConfigs`. Afterwards rerun `check` in a **fresh session**. The rendered - `${user_config.*}` is injected at skill load and each hook receives its - `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a same-session - `check` still reports the OLD value; report the observed effective value, never an - unobserved change. +- toggle off, gitignored files wanted, or the end-of-turn check wanted: set + `bash_format_enabled`, `bash_format_lint_gitignored` or `bash_format_end_of_turn` to `true` + through Claude Code's native flow, interactively with + `/plugin configure bash-format@` or by a headless route, as the marketplace's + plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. After pointing at a remediation, re-run the relevant `check` probe live via Bash (a pre-computed row predates the remediation, so never re-read it) and report its actual result. Never claim resolved on diff --git a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/case.yaml b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/case.yaml index 576250e8364..dcfd68e6247 100644 --- a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/case.yaml +++ b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/case.yaml @@ -5,27 +5,15 @@ context: graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+biome-format@\S+' + pattern: 'claude plugin configure biome-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"biome_format_enabled"\s*:\s*"false"' + pattern: '\bbiome_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+biome-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!biome-format@)(?:\w+|<[^>\s]+>))?)*\s+biome-format@\S+[^\n]*?--config[ =]biome_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says -s defaults to user, so the user passes the scope claude plugin list reports for this plugin; a project or local scope needs the command run from that project''s directory, or the rerun adds a second install record at the scope passed and enables the plugin there; and the value itself lands in user settings whichever route writes it.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -64,3 +52,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+biome\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!biome\-format@)(?:\w+|<[^>\s]+>))?)*\s+biome\-format@\S+(?:\\\n|[^\n])*?--config[ =]biome_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/prompt.md b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/prompt.md index 4aefabfcf58..6d4c1e8d163 100644 --- a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/prompt.md +++ b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 164-189): claude plugin configure --values-stdin with every value a string is preferred (lines 168-171); the claude plugin install --config rerun is the fallback before 2.1.285, and against an installed plugin it prints already installed and still writes the value (lines 171-174); never uninstall, it resets every option (lines 174-176); the value lands in user settings (line 176); configure has no scope flag and runs from the project the plugin is installed in, -s defaults to user, so pass the scope claude plugin list reports, or the rerun adds a second install record at the scope passed (lines 177-181); a same-session check still reports the old value, so rerun check in a fresh session (lines 185-189). The prompt raises each claim. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it, so that legacy expectation is dropped. Legacy 'ends by rerunning check' is the fresh-session rerun of lines 185-189. no-plugin-cli-write guards a claude plugin write the run makes itself (line 184). The scaffold declines the entry so verify prints declined. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: set biome_format_enabled through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. Legacy 'ends by rerunning check' is the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself, since the skill never writes user settings or pluginConfigs. The scaffold declines the entry so verify prints declined. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [biome-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes the headless change to claude plugin configure biome-format@ --values-stdin with {\"biome_format_enabled\": \"false\"}, then the claude plugin install --config biome_format_enabled=false rerun as the fallback that still writes on an installed plugin; rules out uninstalling; gives the scope rule and says the value lands in user settings; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." +expected_outcome: "Routes the change to the native configure flow for biome-format@ with biome_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." --- /biome-format:setup apply diff --git a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/configure-route.json b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/configure-route.json index 17cb8dd7610..c58c7b60b87 100644 --- a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/configure-route.json +++ b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/configure-route.json @@ -2,28 +2,20 @@ "pass": [ { "answer": "`claude plugin configure biome-format@ --values-stdin < values.json`" - }, - { - "answer": "claude plugin configure biome-format@melodic-software --values-stdin", - "why": "real marketplace, two spaces" - }, - { - "answer": "claude plugin configure --values-stdin biome-format@melodic-software", - "why": "the flag before the target" } ], "fail": [ { - "answer": "/plugin configure biome-format@", - "why": "interactive route" + "answer": "claude plugin install biome-format@m --config biome_format_enabled=false", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure biome-format --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { - "answer": "claude plugin configure biome-format@", - "why": "near miss: no --values-stdin" + "answer": "Run `/plugin configure biome-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/no-uninstall-with-reason.json b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/no-uninstall-with-reason.json deleted file mode 100644 index 59b429f0904..00000000000 --- a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall biome-format@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/no-uninstall.json b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/no-uninstall.json new file mode 100644 index 00000000000..55bb52de0dd --- /dev/null +++ b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Simplest route: `claude plugin uninstall biome-format@`, then reinstall with the option set.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/values-false.json b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/values-false.json index 75c8caf46b5..546ea20ece9 100644 --- a/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/values-false.json +++ b/plugins/biome-format/evals/su-apply-headless-toggle-off-no-uninstall/samples/values-false.json @@ -4,22 +4,17 @@ "answer": "`{\"biome_format_enabled\": \"false\"}`" }, { - "answer": "{\"biome_format_enabled\":\"false\"}", - "why": "no space" + "answer": "Set biome_format_enabled to false." } ], "fail": [ - { - "answer": "{\"biome_format_enabled\": false}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "biome_format_enabled=false", - "why": "the --config form, not the JSON" - }, { "answer": "{\"biome_format_enabled\": \"true\"}", "why": "wrong value" + }, + { + "answer": "{\"biome_format_lint_gitignored\": \"false\"}", + "why": "near miss: the wrong key" } ] } diff --git a/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/case.yaml b/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/case.yaml index 419058df28d..08a9a2c31d8 100644 --- a/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/case.yaml +++ b/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/case.yaml @@ -23,7 +23,7 @@ graders: focus: 'last_message' - name: 'opt-in-route' type: 'llm' - criteria: 'The reply names setting biome_format_lint_gitignored to true as the way to act on gitignored files, through the plugin''s own settings flow: /plugin configure, claude plugin configure --values-stdin or a claude plugin install --config rerun.' + criteria: 'The reply names setting biome_format_lint_gitignored to true as the way to act on gitignored files, through the plugin''s own settings flow: /plugin configure, or a headless route it takes from or points at in the plugin-reconfiguration convention.' focus: 'last_message' - name: 'biome-json-not-the-route' type: 'llm' diff --git a/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/prompt.md b/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/prompt.md index 01f857b4e7a..6f7a207ba0c 100644 --- a/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/prompt.md +++ b/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/prompt.md @@ -1,5 +1,5 @@ --- -description: "check, asked why an edit to a gitignored file was left alone. setup SKILL.md step 5 (lines 77-80: the effective biome_format_enabled and biome_format_lint_gitignored values; unexpanded or empty means the default, true and false), with the native reconfiguration routes at lines 164-176, and 'this skill never writes user settings or pluginConfigs' (line 184). hooks/biome-format.sh line 98 skips a gitignored file unless the option is true; README line 137 names the option. The scaffold has .work/ in .gitignore and an unformatted, untracked .work/scratch.ts. The prompt raises the values, the empty reading and biome.json, which the legacy prompt did not. Legacy 'modifies no setting' is enforced by the must-not graders. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "check, asked why an edit to a gitignored file was left alone. setup SKILL.md step 5 (the effective biome_format_enabled and biome_format_lint_gitignored values; unexpanded or empty means the default, true and false), with apply's toggle bullet for the native configure flow and the headless routes cited from the plugin-reconfiguration convention, and 'this skill never writes user settings or pluginConfigs'. hooks/biome-format.sh line 98 skips a gitignored file unless the option is true; README line 137 names the option. The scaffold has .work/ in .gitignore and an unformatted, untracked .work/scratch.ts. The prompt raises the values, the empty reading and biome.json, which the legacy prompt did not. Legacy 'modifies no setting' is enforced by the must-not graders. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [biome-format, setup, check, read-only, must-not, no-network] max_turns: 14 timeout_seconds: 360 diff --git a/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/samples/opt-in-route.json b/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/samples/opt-in-route.json index 5d126c49907..a2374cf048f 100644 --- a/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/samples/opt-in-route.json +++ b/plugins/biome-format/evals/su-check-gitignored-option-not-a-defect/samples/opt-in-route.json @@ -1,7 +1,7 @@ { "pass": [ { - "answer": "Set biome_format_lint_gitignored to true with `/plugin configure biome-format@` or `claude plugin configure biome-format@ --values-stdin`." + "answer": "Set biome_format_lint_gitignored to true with `/plugin configure biome-format@`, or headless by a route from the marketplace's plugin-reconfiguration convention." } ], "fail": [ diff --git a/plugins/biome-format/reference/plugin-reconfiguration.md b/plugins/biome-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/biome-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/biome-format/skills/setup/SKILL.md b/plugins/biome-format/skills/setup/SKILL.md index 71c134535c5..9c658355fc3 100644 --- a/plugins/biome-format/skills/setup/SKILL.md +++ b/plugins/biome-format/skills/setup/SKILL.md @@ -22,6 +22,12 @@ the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -171,32 +177,27 @@ command's exit code alone. For everything else `apply` only points: - missing `jq` / Bash: platform install instructions from the README Requirements section; this skill never installs system packages. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive - `/plugin configure biome-format@` any time. Headless, preferred on Claude Code - 2.1.285 or later: `claude plugin configure biome-format@ --values-stdin < values.json`, - where `values.json` holds `{"biome_format_enabled": "true"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install biome-format@ -s --config biome_format_enabled=true` - (repeatable per key); against an already-installed plugin it prints `already installed` - **and still writes the value**. Do **not** uninstall to reconfigure: that drops this plugin's - entire stored `pluginConfigs` entry, resetting every option in the README's Options reference - to its manifest default. The value lands in user settings whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, `-s` defaults to `user`; pass the scope `claude plugin list` reports for - this plugin, and run from that project's directory for a `project`/`local` scope, or the rerun - adds a second install record at the scope passed and enables the plugin there; from the home - directory pass `user`. Check each - route's own failure signal: `--values-stdin` rejects a bad value with exit 1 and saves nothing, - but stores an empty string as `""`; the `--config` rerun prints a warning for a rejected value - yet exits 0, so read its output. This skill never writes user settings or - `pluginConfigs`. Afterwards rerun `check` in a **fresh session**. The rendered - `${user_config.*}` is injected at skill load and each hook receives its - `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a same-session - `check` still reports the OLD value; report the observed effective value, never an - unobserved change. +- toggle off: set `biome_format_enabled` to `true` through Claude Code's native flow, + interactively with `/plugin configure biome-format@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - no Biome config: offer to create a minimal `biome.json` in the repository root only when explicitly asked. The plugin imposes no rules of its own. diff --git a/plugins/bugs/reference/plugin-reconfiguration.md b/plugins/bugs/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/bugs/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/bugs/skills/setup/SKILL.md b/plugins/bugs/skills/setup/SKILL.md index 40db0b2db45..d77516ee041 100644 --- a/plugins/bugs/skills/setup/SKILL.md +++ b/plugins/bugs/skills/setup/SKILL.md @@ -43,28 +43,27 @@ Modify nothing. Report both surfaces, then one remediation line per gap. committed alongside code. For the repository option, inspect the consumer's `CLAUDE.md`, `AGENTS.md`, and existing report or artifact directories and recommend one portable location. Never recommend a machine-absolute team path. -4. If the recommended value differs, reconfigure through Claude Code's native flow per the marketplace's - plugin-reconfiguration convention, which owns the verified-version record: interactive - `/plugin configure bugs@` any time. Headless, preferred: - `claude plugin configure bugs@ --values-stdin < values.json`, run from the project - the plugin is installed in, where `values.json` holds `{"output_dir": ""}`. Headless - fallback, for a Claude Code release without `plugin configure`: `claude plugin install - bugs@ -s --config output_dir=`. Always pass `-s`, with the scope - `claude plugin list` reports, from that project's directory for a `project`/`local` scope; from - the home directory pass `user`. Whichever route ran, read both its exit code and its output - before reporting the value saved. Never uninstall to reconfigure. Then rerun `check` in a fresh - session and report the observed destination. Convention: - . - - - **Pointer**: when checking how a route reports a rejected value, what the rerun prints, the - `-s` default, where the value is stored, or what an uninstall deletes, fetch - [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), - [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install), - [§ Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) - and [§ What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps) - live. **As of**: 2026-11-10. **Recheck trigger**: one of those sections changes how a route - reports a rejection, the rerun's output, the `-s` default, the scope it installs at, where a - value is stored, or what an uninstall deletes. +4. If the recommended value differs, set `output_dir` to it through Claude Code's native flow, + interactively with `/plugin configure bugs@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Then rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed destination, never + an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. ### B. `docs/conventions/bugs.yaml` (tracked lane config) @@ -161,9 +160,9 @@ by re-reading it, for `output_dir` by a rerun in a fresh session. and [skills § Available string substitutions](https://code.claude.com/docs/en/skills#available-string-substitutions) live (as of 2026-11-03; recheck trigger: either section changes when a substitution is applied). - **Never uninstall to reconfigure.** Reconfigure through `check` step A4; what an uninstall deletes - is upstream's, at the pointer under that step. -- **Read both the exit code and the output of the headless route that ran** before reporting a - value saved (pointer under `check` step A4). + is linked from the convention section that step cites. +- **Check the headless route's own failure signal** before reporting a value saved, as the + convention's caveats section cited in `check` step A4 gives it. - **The ignore probe alone blesses an unshared file.** An untracked, unignored file returns the same empty `check-ignore` output as a healthy tracked one; the `ls-files` probe is what catches it. - **A file `apply` just scaffolded is untracked until staged.** `git ls-files --error-unmatch` will diff --git a/plugins/code-tidying/reference/plugin-reconfiguration.md b/plugins/code-tidying/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/code-tidying/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/code-tidying/skills/setup/SKILL.md b/plugins/code-tidying/skills/setup/SKILL.md index 165ca895029..cf0e1ac8104 100644 --- a/plugins/code-tidying/skills/setup/SKILL.md +++ b/plugins/code-tidying/skills/setup/SKILL.md @@ -153,35 +153,29 @@ Re-running `apply` after everything passes changes nothing and reports "already Not `apply` surface: Claude Code owns the storage, and this contract forbids setup to write `pluginConfigs`, so `check` reports the observed value and routes the change. Reconfigure through -Claude Code's native flow, per the marketplace's -[plugin-reconfiguration convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/plugin-reconfiguration/README.md) -(which owns the verified-version record): interactive `/plugin configure code-tidying@` -any time. Headless, preferred on Claude Code 2.1.285 or later, run from the project the plugin is -installed in (it has no scope flag): - -```shell -claude plugin configure code-tidying@ --values-stdin < values.json -``` - -with `values.json` holding `{"hard_exclusions": "advisory"}` (every value a string; keys left out -keep their saved values). It rejects a bad value with exit 1 and saves nothing, but stores an empty -string as `""`. Headless fallback, before 2.1.285: - -```shell -claude plugin install code-tidying@ -s --config hard_exclusions=advisory -``` - -Against an installed plugin this prints `already installed` and still writes the value. Pass the -scope `claude plugin list` reports, or the rerun adds an install record at the scope passed and -enables the plugin there; from the home directory pass `user`. A rejected value prints a warning -yet exits 0, so read the output. Either route stores the value in user settings. Do not uninstall -to reconfigure: that drops this plugin's entire stored `pluginConfigs` entry, resetting every -option in the README's Options reference to its manifest default. The running -session is not re-read, so rerun `check` in a fresh session before expecting the new behavior, and -report the observed value. `hard_exclusions` takes `enforce` (default, every GLOBAL HARD path -entry blocks) or `advisory` (every one is reported and none blocks); any other value is read as +Claude Code's native flow, interactively with `/plugin configure code-tidying@` or by +a headless route, as the marketplace's plugin-reconfiguration convention gives them in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record). Apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never +writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a +same-session `check` can still show the old value, and report the observed effective value, never +an unobserved change. `hard_exclusions` takes `enforce` (default, every GLOBAL HARD path entry +blocks) or `advisory` (every one is reported and none blocks); any other value is read as `enforce`. The full option table is in the plugin README. +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. + ## Output Tracked `.claude/tidy-lanes/.md` file(s) in the consuming repo, plus a one-paragraph summary @@ -200,9 +194,11 @@ names it and the globs it lifts. ## Gotchas -- **Do not uninstall to reconfigure.** It drops this plugin's entire stored `pluginConfigs` - entry; the install command's `already installed` short-circuit is about the install, not the - config write. +- **Do not uninstall to reconfigure.** The plugin-reconfiguration convention's + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries) + links what an uninstall deletes, and + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + covers what an install rerun does to stored options. - **A copied bundled section is frozen** at its copy-time value, while an omitted one keeps inheriting bundled improvements. Write only the sections that diverge. - **Gitignoring a path the team already tracks does not make it personal**: indexed files remain diff --git a/plugins/context-budget/evals/su-headless-toggle-configure/case.yaml b/plugins/context-budget/evals/su-headless-toggle-configure/case.yaml index 18df7f29aeb..1a5d2dde21d 100644 --- a/plugins/context-budget/evals/su-headless-toggle-configure/case.yaml +++ b/plugins/context-budget/evals/su-headless-toggle-configure/case.yaml @@ -3,28 +3,19 @@ name: su-headless-toggle-configure graders: - name: configure-route type: regex - pattern: 'claude plugin configure context-budget@\S+\s+--values-stdin' + pattern: 'claude plugin configure context-budget@\S+' target: 'last_message' - name: string-false-value type: regex - pattern: '"settings_write_ask_enabled"\s*:\s*"false"' + pattern: '\bsettings_write_ask_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: fallback-after-configure - type: regex - pattern: 'claude plugin configure context-budget@\S+\s+--values-stdin[\s\S]*?claude plugin install context-budget@\S+[^\n]*?--config[ =]settings_write_ask_enabled=false\b' - target: 'last_message' - - name: already-installed-still-writes - type: regex - pattern: '\balready installed\b[^\n]{0,160}\b(?:still|also)\b[^\n]{0,40}\bwrites?\b|\bstill writes?\b[^\n]{0,160}\balready installed\b' - flags: 'i' - target: 'last_message' - - name: no-uninstall-with-reason + - name: no-uninstall type: llm - criteria: 'The reply does not tell the user to uninstall the plugin (for example claude plugin uninstall, then reinstall) in order to reconfigure it, and says why: uninstalling drops the plugin''s whole stored pluginConfigs entry and resets every option in the README Options reference to its manifest default.' + criteria: 'The reply does not tell the user to uninstall the plugin (for example claude plugin uninstall, then reinstall) in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: cites-reconfiguration-convention type: regex - pattern: '\bplugin[- ]reconfiguration\b' + pattern: '\bplugin[- ]reconfiguration\b|\breconfiguration[- ](?:convention|contract|routes|guide)\b' flags: 'i' target: 'last_message' - name: no-restated-versions @@ -33,18 +24,27 @@ graders: flags: 'i' match: 'not_contains' target: 'last_message' - - name: scope-rule - type: llm - criteria: 'The reply says the value lands in user settings whichever route writes it; that claude plugin configure has no scope flag, so it is run from the project the plugin is installed in; that for the install rerun the user passes the scope claude plugin list reports for this install (user, project or local), because -s places enabledPlugins and a different scope adds an install record and enables the plugin there; and that from the home directory, where one settings file can be labeled both user and project, the scope to pass is user.' - focus: 'last_message' - name: fresh-session-observed-value type: llm - criteria: 'The reply does not claim the checkpoint is now off. It tells the user to rerun check in a fresh session, because the toggle value is injected when the skill loads and a same-session check still reports the old value, and to report the effective value that check observes.' + criteria: 'The reply does not claim the checkpoint is now off. It tells the user to confirm the change by rerunning the setup check in a fresh session, because a same-session check can still report the old value.' focus: 'last_message' - name: no-plugin-command-run type: tool_used tool: 'Bash' - input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b' + input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b(?!\s+--help\b)' min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+context\-budget@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!context\-budget@)(?:\w+|<[^>\s]+>))?)*\s+context\-budget@\S+(?:\\\n|[^\n])*?--config[ =]settings_write_ask_enabled=false\b' + target: 'last_message' + - name: 'already-installed-still-writes' + type: 'regex' + pattern: '\balready[- ]installed\b(?:(?!\bnot\b|n[''’]t\b|\bnothing\b)[^\n]){0,160}?\b(?:writes?|saves?)\b|\bstill (?:writes?|saves?)\b[^\n]{0,160}\balready[- ]installed\b|\b(?:writes?|saves?)\b[^\n]{0,40}\b(?:on|against|to)\s+an?\s+already[- ]installed\b' + flags: 'i' + target: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/context-budget/evals/su-headless-toggle-configure/prompt.md b/plugins/context-budget/evals/su-headless-toggle-configure/prompt.md index e0591efb55d..1a421f52228 100644 --- a/plugins/context-budget/evals/su-headless-toggle-configure/prompt.md +++ b/plugins/context-budget/evals/su-headless-toggle-configure/prompt.md @@ -1,11 +1,11 @@ --- -description: 'Headless toggle change routes to claude plugin configure, never an uninstall. setup is user-invoked only, so the case starts with its slash command and carries no Skill grader. setup SKILL.md lines 107-130: per the plugin-reconfiguration convention (which owns the verified-version record), headless preferred is claude plugin configure context-budget@ --values-stdin with {"settings_write_ask_enabled": "false"} (every value a string; 2.1.285 or later); fallback is the claude plugin install ... -s --config settings_write_ask_enabled=false rerun, which on an installed plugin prints already installed and still writes the value; never uninstall, which drops the whole pluginConfigs entry and resets every option in the README Options reference; pass the scope claude plugin list reports (user from the home directory), -s places enabledPlugins, and the value lands in user settings; rerun check in a fresh session and report the observed value. Line 115: print the commands, never run either. no-restated-versions fails a Claude Code version other than 2.1.285 stated as verified, measured, tested or confirmed; it is confined to those words because check may report the host''s probed version (line 69). Needs --allow-tools Bash.' +description: 'Headless toggle change routes to the native configure flow, never an uninstall. setup is user-invoked only, so the case starts with its slash command and carries no Skill grader. setup SKILL.md remediation''s toggle bullet: set settings_write_ask_enabled through the native configure flow, with the headless routes and their scope and failure-signal caveats cited from the plugin-reconfiguration convention (which owns the verified-version record) rather than restated (cites-reconfiguration-convention); print the route, never run one; never uninstall to reconfigure; rerun check in a fresh session and report the observed value. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, already-installed-still-writes and scope-rule grade them there. no-restated-versions fails a Claude Code version other than 2.1.285 stated as verified, measured, tested or confirmed; it is confined to those words because check may report the host''s probed version. Needs --allow-tools Bash.' tags: [context-budget, setup, outcome, reconfigure, must-not, no-network] runs: 3 max_turns: 15 timeout_seconds: 600 allowed_tools: [Skill, Read, Glob, Grep, Bash] -expected_outcome: 'Routes the change to claude plugin configure context-budget@ --values-stdin with {"settings_write_ask_enabled": "false"}, then the claude plugin install --config rerun as the fallback that still writes on an installed plugin; rules out uninstalling and says why; cites the plugin-reconfiguration convention; gives the scope rule; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' +expected_outcome: 'Routes the change to the native configure flow for context-budget@ with settings_write_ask_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' --- /context-budget:setup I want settings_write_ask_enabled set to false, headless. I cannot use the interactive configure prompt from this session. diff --git a/plugins/context-budget/evals/su-headless-toggle-configure/samples/already-installed-still-writes.json b/plugins/context-budget/evals/su-headless-toggle-configure/samples/already-installed-still-writes.json index 7f217aa45c4..adf6635da28 100644 --- a/plugins/context-budget/evals/su-headless-toggle-configure/samples/already-installed-still-writes.json +++ b/plugins/context-budget/evals/su-headless-toggle-configure/samples/already-installed-still-writes.json @@ -3,6 +3,18 @@ { "answer": "Against the installed plugin it prints `already installed` and still writes the value.", "why": "both" + }, + { + "answer": "The rerun is the fallback because it saves the value on an already-installed plugin." + }, + { + "answer": "Already installed: the rerun still saves the value." + }, + { + "answer": "On an already-installed plugin this prints `already installed` and only saves the value." + }, + { + "answer": "On a plugin that's already installed, a rerun at the same scope prints `already installed` and saves only the option." } ], "fail": [ @@ -13,6 +25,18 @@ { "answer": "It still writes the value.", "why": "no already installed" + }, + { + "answer": "Fallback: claude plugin install @ -s --config KEY=VALUE.", + "why": "gives the fallback but omits that it saves on an already-installed plugin" + }, + { + "answer": "If it is already installed, the rerun does not save the value.", + "why": "says nothing is saved" + }, + { + "answer": "Already installed: the rerun won't write anything.", + "why": "says nothing is written" } ] } diff --git a/plugins/context-budget/evals/su-headless-toggle-configure/samples/configure-route.json b/plugins/context-budget/evals/su-headless-toggle-configure/samples/configure-route.json index d747094e0f0..a0529e0e100 100644 --- a/plugins/context-budget/evals/su-headless-toggle-configure/samples/configure-route.json +++ b/plugins/context-budget/evals/su-headless-toggle-configure/samples/configure-route.json @@ -2,17 +2,21 @@ "pass": [ { "answer": "claude plugin configure context-budget@melodic --values-stdin < values.json", - "why": "route" + "why": "headless route" } ], "fail": [ { - "answer": "/plugin configure context-budget@melodic", - "why": "interactive" + "answer": "claude plugin install context-budget@melodic --config settings_write_ask_enabled=false", + "why": "an install rerun, not the configure flow" }, { - "answer": "claude plugin configure context-budget@melodic", - "why": "no --values-stdin" + "answer": "claude plugin configure context-budget --values-stdin", + "why": "near miss: no marketplace" + }, + { + "answer": "Run `/plugin configure context-budget@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/context-budget/evals/su-headless-toggle-configure/samples/no-plugin-command-run.json b/plugins/context-budget/evals/su-headless-toggle-configure/samples/no-plugin-command-run.json index ddb74882cc3..b6bcac14217 100644 --- a/plugins/context-budget/evals/su-headless-toggle-configure/samples/no-plugin-command-run.json +++ b/plugins/context-budget/evals/su-headless-toggle-configure/samples/no-plugin-command-run.json @@ -23,6 +23,18 @@ } ], "why": "version" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "claude plugin configure --help 2>&1 | head -40", + "description": "Show configure flags" + } + } + ], + "why": "a read-only --help probe" } ], "fail": [ diff --git a/plugins/context-budget/evals/su-headless-toggle-configure/samples/string-false-value.json b/plugins/context-budget/evals/su-headless-toggle-configure/samples/string-false-value.json index 7736d900c0f..a2a99dc5ca4 100644 --- a/plugins/context-budget/evals/su-headless-toggle-configure/samples/string-false-value.json +++ b/plugins/context-budget/evals/su-headless-toggle-configure/samples/string-false-value.json @@ -2,13 +2,21 @@ "pass": [ { "answer": "{\"settings_write_ask_enabled\": \"false\"}", - "why": "string" + "why": "JSON form" + }, + { + "answer": "Set settings_write_ask_enabled to false.", + "why": "key and value in prose" } ], "fail": [ { - "answer": "{\"settings_write_ask_enabled\": false}", - "why": "boolean" + "answer": "{\"settings_write_ask_enabled\": \"true\"}", + "why": "wrong value" + }, + { + "answer": "Turn the checkpoint off.", + "why": "near miss: names neither key nor value" } ] } diff --git a/plugins/context-budget/reference/plugin-reconfiguration.md b/plugins/context-budget/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/context-budget/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/context-budget/skills/setup/SKILL.md b/plugins/context-budget/skills/setup/SKILL.md index d6447fd822b..35abe069838 100644 --- a/plugins/context-budget/skills/setup/SKILL.md +++ b/plugins/context-budget/skills/setup/SKILL.md @@ -27,6 +27,12 @@ shows `present`, `absent`, or `unavailable` when the plugin data directory canno A `node` or `claude` row reading `[shell command execution disabled by policy]` carries no result: run that probe via Bash instead. The SDK row is never re-probed: read it as `unavailable`. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Check-only setup under the Check-only carve-out (`docs/plugin-philosophy.md` "Setup is explicit @@ -110,30 +116,28 @@ Close by pointing at each resolution. A rerun after everything passes changes no New-Item -ItemType Directory -Force -Path "${CLAUDE_PLUGIN_DATA}\sdk" | Out-Null; npm install --prefix "${CLAUDE_PLUGIN_DATA}\sdk" @anthropic-ai/claude-agent-sdk ``` -- **Toggle off (or on):** reconfigure through Claude Code's native flow, per the - [plugin-reconfiguration convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/plugin-reconfiguration/README.md), - which owns the verified-version record: interactive - `/plugin configure context-budget@`. Headless, preferred on Claude Code 2.1.285 or - later: `claude plugin configure context-budget@ --values-stdin < values.json`, where - `values.json` holds `{"settings_write_ask_enabled": "true"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install context-budget@ -s --config settings_write_ask_enabled=true` - (repeatable per key). Print these commands; never run either, because both write `pluginConfigs`. - - Against an installed plugin the `--config` rerun prints `already installed` **and still writes - the value**. - - Do **not** uninstall to reconfigure: that drops the plugin's whole `pluginConfigs` entry, - resetting every option in the README's Options reference to its manifest default. - - The value lands in user settings whichever route writes it. `claude plugin configure` has no - scope flag, so run it from the project the plugin is installed in. For the rerun, pass the - scope `claude plugin list` reports for this install (`user`, `project` or `local`). `-s` - places `enabledPlugins`; a different scope adds an install record and enables the plugin - there. From the home directory the list can label one settings file as both `user` and - `project`; pass `user`. - - Check each route's own failure signal: `--values-stdin` rejects a bad value with exit 1 and - saves nothing, but stores an empty string as `""`; the `--config` rerun prints a warning for a - rejected value yet exits 0, so read its output. - - Afterwards rerun `check` in a **fresh session**: a same-session `check` still reports the old - value. Report the observed value, never an unobserved change. +- **Toggle off (or on):** set `settings_write_ask_enabled` through Claude Code's native flow, + interactively with `/plugin configure context-budget@` or by a headless route, as + the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Print the route for the operator; never run a headless + route, because each writes `pluginConfigs`, which this skill never writes, nor user settings. + Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. Afterwards rerun + `check` in a **fresh session**, since a same-session `check` can still show the old value, and + report the observed effective value, never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. ## Next diff --git a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/case.yaml b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/case.yaml index 41a3b2bdfbf..39bf161ad28 100644 --- a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/case.yaml +++ b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/case.yaml @@ -3,31 +3,19 @@ name: su-apply-headless-master-key-configure graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure desktop-notification@\S+\s+--values-stdin' + pattern: 'claude plugin configure desktop-notification@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '\"desktop_notification_enabled\"\s*:\s*\"false\"' + pattern: '\bdesktop_notification_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure desktop-notification@\S+\s+--values-stdin[\s\S]*?claude plugin install desktop-notification@\S+[^\n]*?--config[ =]desktop_notification_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - name: 'one-channel-own-key' type: 'llm' criteria: 'The reply says one channel is muted the same way through that channel''s own key, naming at least one of desktop_notification_bell_enabled, desktop_notification_terminal_notify_enabled or desktop_notification_os_toast_enabled.' focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says to run the command from the project the plugin is installed in (claude plugin configure has no scope flag), and that the install rerun''s -s flag defaults to user, so the user passes the scope claude plugin list reports for this plugin.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -52,3 +40,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+desktop\-notification@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!desktop\-notification@)(?:\w+|<[^>\s]+>))?)*\s+desktop\-notification@\S+(?:\\\n|[^\n])*?--config[ =]desktop_notification_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/prompt.md b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/prompt.md index 2f784f59e21..97bb4d1c338 100644 --- a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/prompt.md +++ b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 110-135): claude plugin configure --values-stdin with every value a string is preferred; the claude plugin install --config rerun is the fallback before 2.1.285 and against an installed plugin prints already installed and still writes (lines 119-120); keys are repeatable per key (lines 116-119); never uninstall (lines 120-122); configure has no scope flag and runs from the project the plugin is installed in, -s defaults to user, so pass the scope claude plugin list reports (lines 123-126); a same-session check reports the old value, so rerun check in a fresh session and report the observed value (lines 131-135). The prompt raises each claim. Legacy 'cites the plugin-reconfiguration convention' and its record is dropped: the skill cites it as its own source and never tells the reply to name it. Legacy 'resetting all four toggles' now reads 'every option' (lines 121-122). Legacy 'ends by rerunning check' is now the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: set the toggle's key through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; one channel is muted through its own key (check step 5 names them); never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session and report the observed value. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. Legacy 'ends by rerunning check' is now the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." tags: [desktop-notification, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes to claude plugin configure desktop-notification@ --values-stdin with {\"desktop_notification_enabled\": \"false\"}, then the claude plugin install --config rerun that still writes on an installed plugin; one channel through its own key; rules out uninstalling; gives the scope rule; rerun check in a fresh session and report the observed value; runs no claude plugin write itself." +expected_outcome: "Routes to the native configure flow for desktop-notification@ with desktop_notification_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; one channel through its own key; rules out uninstalling; rerun check in a fresh session and report the observed value; runs no claude plugin write itself." --- /desktop-notification:setup apply diff --git a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/configure-route.json b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/configure-route.json index 6a7d11dc7f0..1dd8c4c8300 100644 --- a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/configure-route.json +++ b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/configure-route.json @@ -2,24 +2,20 @@ "pass": [ { "answer": "`claude plugin configure desktop-notification@ --values-stdin < values.json`" - }, - { - "answer": "claude plugin configure desktop-notification@melodic-software --values-stdin", - "why": "real marketplace, two spaces" } ], "fail": [ { - "answer": "/plugin configure desktop-notification@", - "why": "interactive route" + "answer": "claude plugin install desktop-notification@m --config desktop_notification_enabled=false", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure desktop-notification --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { - "answer": "", - "why": "empty" + "answer": "Run `/plugin configure desktop-notification@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/no-uninstall-with-reason.json b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/no-uninstall-with-reason.json deleted file mode 100644 index f99a78e6bf9..00000000000 --- a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - }, - { - "answer": "No: an uninstall wipes its pluginConfigs entry, so every toggle goes back to its default." - }, - { - "answer": "Avoid uninstall and reinstall. It discards all stored options, which return to the manifest defaults." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall desktop-notification@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - }, - { - "answer": "Don't uninstall: it needs a restart.", - "why": "near miss: wrong reason" - }, - { - "answer": "Uninstall is fine, though you will lose your options.", - "why": "near miss: allows it" - }, - { - "answer": "Don't uninstall: it only resets the toggle you are flipping.", - "why": "near miss: understates the reset" - } - ] -} diff --git a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/no-uninstall.json b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/no-uninstall.json new file mode 100644 index 00000000000..3c8b61c4d77 --- /dev/null +++ b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Simplest route: `claude plugin uninstall desktop-notification@`, then reinstall with the option set.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/values-false.json b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/values-false.json index 6b740221121..8211e509679 100644 --- a/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/values-false.json +++ b/plugins/desktop-notification/evals/su-apply-headless-master-key-configure/samples/values-false.json @@ -1,25 +1,20 @@ { "pass": [ { - "answer": "{\"desktop_notification_enabled\": \"false\"}" + "answer": "`{\"desktop_notification_enabled\": \"false\"}`" }, { - "answer": "{\"desktop_notification_enabled\":\"false\"}", - "why": "no space" + "answer": "Set desktop_notification_enabled to false." } ], "fail": [ { - "answer": "{\"desktop_notification_enabled\": false}", - "why": "a bare boolean: every value is a string" + "answer": "{\"desktop_notification_enabled\": \"true\"}", + "why": "wrong value" }, { "answer": "{\"desktop_notification_bell_enabled\": \"false\"}", - "why": "a channel key" - }, - { - "answer": "{\"\": \"false\"}", - "why": "a placeholder" + "why": "near miss: a channel key, not the master key" } ] } diff --git a/plugins/desktop-notification/reference/plugin-reconfiguration.md b/plugins/desktop-notification/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/desktop-notification/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/desktop-notification/skills/setup/SKILL.md b/plugins/desktop-notification/skills/setup/SKILL.md index 5638ea565c6..b16e72313ee 100644 --- a/plugins/desktop-notification/skills/setup/SKILL.md +++ b/plugins/desktop-notification/skills/setup/SKILL.md @@ -23,6 +23,12 @@ of re-issuing them. The `node` and `jq` rows show the tool's path, or `absent` w A row reading `[shell command execution disabled by policy]` carries no result: run that probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -103,32 +109,27 @@ nothing and writes nothing, so every remediation is a pointer the user acts on: section. This skill never installs system packages. - **missing `notify-send`** (Linux, `os_toast` enabled). `sudo apt install libnotify-bin` (Debian/Ubuntu) or `sudo dnf install libnotify` (Fedora). Guidance only. The user runs it. -- **a toggle is off**. Reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention, which owns the verified-version record - (): - interactive `/plugin configure desktop-notification@` any time. Headless, preferred - on Claude Code 2.1.285 or later: - `claude plugin configure desktop-notification@ --values-stdin < values.json`, where - `values.json` holds `{"": "true"}` (every value a string, booleans included; keys left out - keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install desktop-notification@ -s --config =true` - (repeatable per key); against an already-installed plugin it prints `already installed` **and - still writes the value**. Do **not** uninstall to reconfigure: uninstalling drops this plugin's - entire stored `pluginConfigs` entry, resetting every option in the README's Options reference - to its manifest default. The value lands in user settings whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, `-s` defaults to `user`; pass the install scope `claude plugin list` reports - for this plugin, and run from that project's directory for a `project`/`local` scope; from the - home directory pass `user`. A rerun at - another scope adds an install record at that scope and enables the plugin there (measured in - both directions). Check each route's own failure signal: `--values-stdin` rejects a bad value - with exit 1 and saves nothing, but stores an empty string as `""`; the `--config` rerun prints a - warning for a rejected value yet exits 0, so read its output. These options are personal `userConfig` - values, so this skill never writes user settings or `pluginConfigs`. Afterwards rerun `check` - in a **fresh session**. The rendered `${user_config.*}` is injected at skill load and each - hook receives its `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a - same-session `check` still reports the OLD value; report the observed effective value, never an - unobserved change. +- **a toggle is off**. Set that toggle's key to `true` through Claude Code's native flow, + interactively with `/plugin configure desktop-notification@` or by a headless route, + as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. These options are + personal `userConfig` values, so this skill never writes user settings or `pluginConfigs`. + Afterwards rerun `check` in a **fresh session**, since a same-session `check` can still show the + old value, and report the observed effective value, never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. After the user reports acting on any system-tool remediation, re-run the relevant `check` probe live via Bash (a pre-computed row predates the remediation) and report its actual diff --git a/plugins/discipline/reference/plugin-reconfiguration.md b/plugins/discipline/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/discipline/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/discipline/skills/setup/SKILL.md b/plugins/discipline/skills/setup/SKILL.md index 534c52c51af..78409cdcfec 100644 --- a/plugins/discipline/skills/setup/SKILL.md +++ b/plugins/discipline/skills/setup/SKILL.md @@ -102,25 +102,26 @@ Official contract: ): - interactive `/plugin configure discipline@` any time. Headless, preferred on - Claude Code 2.1.285 or later: - `claude plugin configure discipline@ --values-stdin < values.json`, where - `values.json` holds `{"": ""}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback, before 2.1.285: rerun - `claude plugin install discipline@ -s --config =` (repeatable - per key); against an already-installed plugin it prints `already installed` and still writes - the value. Never uninstall to reconfigure: that drops the whole stored `pluginConfigs` entry, - resetting every option to its manifest default. Scope: the option value lands in user settings - whichever route writes it. `claude plugin configure` has no scope flag, so run it from the - project the plugin is installed in. For the rerun, pass the scope `claude plugin list` reports - for this plugin (from the home directory pass `user`); a rerun at another scope adds an install - record there and enables the plugin. Check each route's own failure signal: `--values-stdin` - rejects a bad value with exit 1 and saves nothing, but stores an empty string as `""`; the - `--config` rerun prints a warning for a rejected value yet exits 0, so read its output. Claude - Code owns persistence. Do not hand-edit any `pluginConfigs` key. +8. To change or clear any personal option, direct the user to Claude Code's native flow, + interactively with `/plugin configure discipline@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. Claude Code owns + persistence: this skill never writes user settings or `pluginConfigs`, and never hand-edits any + `pluginConfigs` key. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. 9. Tell the user to rerun `check` after reconfiguration **in a fresh session**, and report the OBSERVED effective values from that fresh run, never an unobserved change. Substitution of the rendered `${user_config.*}` values is documented at diff --git a/plugins/disk-hygiene/reference/plugin-reconfiguration.md b/plugins/disk-hygiene/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/disk-hygiene/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/disk-hygiene/skills/setup/SKILL.md b/plugins/disk-hygiene/skills/setup/SKILL.md index 5b001253abd..f913828d476 100644 --- a/plugins/disk-hygiene/skills/setup/SKILL.md +++ b/plugins/disk-hygiene/skills/setup/SKILL.md @@ -197,32 +197,27 @@ tool or an OS capability, so `apply` installs nothing and writes nothing, it onl - missing git (worktree targets): platform install instructions. - missing `node` or no bash the launcher accepts: the platform's own install channel for Node.js or Git for Windows, or on Windows `CLAUDE_CODE_GIT_BASH_PATH` naming an existing `bash.exe`. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention, which owns the verified-version record - (): - interactive `/plugin configure disk-hygiene@` any time. Headless, preferred on - Claude Code 2.1.285 or later: - `claude plugin configure disk-hygiene@ --values-stdin < values.json`, where - `values.json` holds `{"disk_hygiene_enabled": "true"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install disk-hygiene@ -s --config disk_hygiene_enabled=true` - (repeatable per key); the convention above records how it behaves against an installed plugin. Do **not** uninstall to - reconfigure: this skill never trades the operator's saved options for a reconfigure. For what an - uninstall removes, fetch - [What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps) - live (as of 2026-11-03; recheck when that section changes what it deletes). The value always - lands in user settings, whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, `-s` defaults to `user`; pass the scope `claude plugin list` reports for this - plugin (from the home directory pass `user`), and run from that project's directory for a `project`/`local` scope, or the rerun adds a - second install record at the scope passed and enables the plugin there. Check each route's own - failure signal: `--values-stdin` rejects a bad value with exit 1 and saves nothing, but stores - an empty string as `""`; the `--config` rerun prints a warning for a rejected value yet exits 0, - so read its output. This skill never writes user settings or - `pluginConfigs`. Afterwards rerun `check` in a **fresh session**: a same-session `check` can still - report the OLD value (when a saved value reaches skill content and hook environments: - [Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value), as of 2026-11-10; recheck when - that section changes when a saved value takes effect); report the observed effective value, never an unobserved change. +- toggle off: set `disk_hygiene_enabled` to `true` through Claude Code's native flow, + interactively with `/plugin configure disk-hygiene@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. Re-running `apply` after everything passes changes nothing and reports "already configured". diff --git a/plugins/docs-hygiene/reference/plugin-reconfiguration.md b/plugins/docs-hygiene/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/docs-hygiene/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/docs-hygiene/skills/setup/SKILL.md b/plugins/docs-hygiene/skills/setup/SKILL.md index 92afc30b52b..ad200a630c8 100644 --- a/plugins/docs-hygiene/skills/setup/SKILL.md +++ b/plugins/docs-hygiene/skills/setup/SKILL.md @@ -78,31 +78,27 @@ markdownlint-cli2` in the repository or a global install, then rerun `check`. Wi `/docs-hygiene:compress` stops at its entry point before touching a file. No other skill in this plugin needs the binary. -To change the `userConfig` value, reconfigure through Claude Code's native flow per the -plugin-reconfiguration convention -(, -which owns the verified-version record): `/plugin configure docs-hygiene@` -interactively. Headless, preferred on Claude Code 2.1.285 or later: -`claude plugin configure docs-hygiene@ --values-stdin < values.json`, where -`values.json` holds `{"compress_articles": ""}`. Headless fallback, before 2.1.285: rerun -`claude plugin install docs-hygiene@ -s --config compress_articles=`. - -- Never uninstall to reconfigure. -- Scope: `claude plugin configure` has no scope flag, so run it from the project the plugin is - installed in. For the rerun, pass the scope `claude plugin list` reports (from the home directory - pass `user`); a rerun at another scope adds an install record there and enables the plugin. -- Rendered values are injected at skill load, so rerun `check` in a fresh session after a change - and report the observed value, never an unobserved change. -- Whichever route ran, read both its exit code and its output before reporting the value saved: - the two routes signal a rejected value differently. -- **Pointer**: when checking how a route reports a rejected value, what the rerun prints, where the - value is stored, or what an uninstall deletes, fetch - [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), - [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install), - [§ Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) - and [§ What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps) - live. **As of**: 2026-11-10. **Recheck trigger**: one of those sections changes how a route - reports a rejection, the rerun's output, where a value is stored, or what an uninstall deletes. +To change the `userConfig` value of `compress_articles`, reconfigure through Claude Code's native +flow, interactively with `/plugin configure docs-hygiene@` or by a headless route, as +the marketplace's plugin-reconfiguration convention gives them in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record). Apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never +writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a +same-session `check` can still show the old value, and report the observed effective value, never +an unobserved change. + +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. To set the value for the whole repository instead, use `apply`. diff --git a/plugins/education/evals/su-check-repo-file/samples/all-four-settings.json b/plugins/education/evals/su-check-repo-file/samples/all-four-settings.json index c9376881c2e..6653c425507 100644 --- a/plugins/education/evals/su-check-repo-file/samples/all-four-settings.json +++ b/plugins/education/evals/su-check-repo-file/samples/all-four-settings.json @@ -5,7 +5,7 @@ "why": "all four" }, { - "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nValues are injected at skill load, so a userConfig change shows only in a new session.\n", + "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nA userConfig change shows only in a new session.\n", "why": "all four" } ], diff --git a/plugins/education/evals/su-check-repo-file/samples/effective-rung-repo-layer.json b/plugins/education/evals/su-check-repo-file/samples/effective-rung-repo-layer.json index 11c31349e3f..9e7dcf583dd 100644 --- a/plugins/education/evals/su-check-repo-file/samples/effective-rung-repo-layer.json +++ b/plugins/education/evals/su-check-repo-file/samples/effective-rung-repo-layer.json @@ -5,7 +5,7 @@ "why": "repository file named" }, { - "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nValues are injected at skill load, so a userConfig change shows only in a new session.\n", + "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nA userConfig change shows only in a new session.\n", "why": "repository layer" } ], diff --git a/plugins/education/evals/su-check-repo-file/samples/fresh-session-note.json b/plugins/education/evals/su-check-repo-file/samples/fresh-session-note.json index c6bf1dcaf46..86f0f3ac26f 100644 --- a/plugins/education/evals/su-check-repo-file/samples/fresh-session-note.json +++ b/plugins/education/evals/su-check-repo-file/samples/fresh-session-note.json @@ -5,7 +5,7 @@ "why": "rerun in a fresh session" }, { - "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nValues are injected at skill load, so a userConfig change shows only in a new session.\n", + "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nA userConfig change shows only in a new session.\n", "why": "only in a new session" } ], diff --git a/plugins/education/evals/su-check-repo-file/samples/quiz-policy-on-request.json b/plugins/education/evals/su-check-repo-file/samples/quiz-policy-on-request.json index 6b7140eec97..0c2c93164a4 100644 --- a/plugins/education/evals/su-check-repo-file/samples/quiz-policy-on-request.json +++ b/plugins/education/evals/su-check-repo-file/samples/quiz-policy-on-request.json @@ -5,7 +5,7 @@ "why": "table row" }, { - "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nValues are injected at skill load, so a userConfig change shows only in a new session.\n", + "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nA userConfig change shows only in a new session.\n", "why": "list item" } ], diff --git a/plugins/education/evals/su-check-repo-file/samples/repo-file-pass-peer.json b/plugins/education/evals/su-check-repo-file/samples/repo-file-pass-peer.json index f37cb1eb79a..75eb4480935 100644 --- a/plugins/education/evals/su-check-repo-file/samples/repo-file-pass-peer.json +++ b/plugins/education/evals/su-check-repo-file/samples/repo-file-pass-peer.json @@ -5,7 +5,7 @@ "why": "table row" }, { - "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nValues are injected at skill load, so a userConfig change shows only in a new session.\n", + "answer": "- quiz_policy: on-request (default)\n- report library: plugin data (report_library_dir unset)\n- workspace root: workspace_root unset, ladder applies\n- PASS explain_starting_rung: peer\n- explain_starting_rung resolves to peer (repository layer)\n\nA userConfig change shows only in a new session.\n", "why": "script line" } ], diff --git a/plugins/education/reference/plugin-reconfiguration.md b/plugins/education/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/education/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/education/skills/quiz-me/SKILL.md b/plugins/education/skills/quiz-me/SKILL.md index d0aaa3363dd..1758ba4270a 100644 --- a/plugins/education/skills/quiz-me/SKILL.md +++ b/plugins/education/skills/quiz-me/SKILL.md @@ -27,7 +27,7 @@ behavior. Configure via the `/plugin` dialog, or headless by the routes `/education:setup` `check` step 7 lists, which follow the -[plugin-reconfiguration convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/plugin-reconfiguration/README.md) +[plugin-reconfiguration convention](../../reference/plugin-reconfiguration.md) (it owns the verified-version record). The hardcoded-path guardrails may block a literal non-home `report_library_dir`; when a write is blocked, report the block and fall back to the `${CLAUDE_PLUGIN_DATA}` default rather than working around the diff --git a/plugins/education/skills/setup/SKILL.md b/plugins/education/skills/setup/SKILL.md index f2186518b4e..97b2f21ab14 100644 --- a/plugins/education/skills/setup/SKILL.md +++ b/plugins/education/skills/setup/SKILL.md @@ -80,28 +80,29 @@ Report a PASS/INFO/WARN table. Write nothing. corpus checkout for long-lived recall across machines. For a repository-backed library, inspect the consumer's artifact conventions and recommend one portable location. Never recommend a machine-absolute team path. -7. To change a `userConfig` value, reconfigure through Claude Code's native flow per the - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure education@` any time. - Headless, preferred on Claude Code 2.1.285 or later: - `claude plugin configure education@ --values-stdin < values.json`, where `values.json` - holds `{"quiz_policy": ""}` (every value a string; keys left out keep their saved values). - Headless fallback, before 2.1.285: rerun - `claude plugin install education@ -s --config quiz_policy=` (repeatable - per key); against an already-installed plugin it prints `already installed` and still writes the - value. Never uninstall to reconfigure: that drops the whole stored `pluginConfigs` entry, resetting every - option to its manifest default. The value always lands in user settings, whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed in. - For the rerun, `-s` defaults to `user`; pass the scope `claude plugin list` reports (from the home directory pass `user`), and run - `project`/`local` writes from that project's directory, or the rerun adds a second install record at - the scope passed and enables the plugin there. Check each route's own failure signal: `--values-stdin` - rejects a bad value with exit 1 and saves nothing, but stores an empty string as `""`; the `--config` - rerun prints a warning for a rejected value yet exits 0, so read its output. To set a value for the - whole repository instead, use `apply`. -8. Tell the user to rerun `check` after a `userConfig` change in a **fresh session**, because rendered - values are injected at skill load and a same-session rerun still reports the OLD values. Then report - the observed effective settings. +7. To change a `userConfig` value such as `quiz_policy`, reconfigure through Claude Code's native + flow, interactively with `/plugin configure education@` or by a headless route, as + the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. To set a value for the whole repository instead, use + `apply`. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. +8. Tell the user to rerun `check` after a `userConfig` change in a **fresh session**, since a + same-session `check` can still show the old value. Then report the observed effective settings, + never an unobserved change. ## `apply` (writes `docs/conventions/education.yaml`, and the step 5 region and ignore line on a yes) diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/case.yaml b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/case.yaml index a4fc94a7c77..ab81e46abb4 100644 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/case.yaml +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/case.yaml @@ -3,23 +3,15 @@ name: 'setup-headless-toggle-off-configures-never-uninstalls' graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure eol-normalizer@\S+\s+--values-stdin' + pattern: 'claude plugin configure eol-normalizer@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"eol_normalizer_enabled"\s*:\s*"false"' + pattern: '\beol_normalizer_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure eol-normalizer@\S+\s+--values-stdin[\s\S]*?claude plugin install eol-normalizer@\S+[^\n]*?--config[ =]eol_normalizer_enabled=false\b' - target: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-default-user' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says the install rerun''s -s flag defaults to user, so the user passes the scope claude plugin list reports for this plugin, and runs the command from the project''s directory for a project or local scope.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -28,7 +20,7 @@ graders: - name: 'no-plugin-cli-write' type: 'tool_used' tool: 'Bash' - input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b' + input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b(?!\s+--help\b)' min: 0 max: 0 arm: 'both' @@ -44,3 +36,11 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+eol\-normalizer@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!eol\-normalizer@)(?:\w+|<[^>\s]+>))?)*\s+eol\-normalizer@\S+(?:\\\n|[^\n])*?--config[ =]eol_normalizer_enabled=false\b' + target: 'last_message' + - name: 'scope-default-user' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/matchers.json b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/matchers.json index db8928b2b6b..13de48b184f 100644 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/matchers.json +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/matchers.json @@ -3,6 +3,6 @@ { "use": "command_position_multishell" }, - "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b" + "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b(?!\\s+--help\\b)" ] } diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/prompt.md b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/prompt.md index fe3b74e6448..b6c9f74ae71 100644 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/prompt.md +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: claude plugin configure --values-stdin preferred (2.1.285 or later), the claude plugin install --config rerun as fallback, never uninstall (it resets every option), -s defaults to user, and a same-session check still reports the old value, so rerun check in a fresh session. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit (Write and Edit are granted so the must-not graders can fail)." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: reconfigure through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure and scope-default-user grade them there. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit (Write and Edit are granted so the must-not graders can fail)." tags: ['setup', 'apply', 'reconfigure', 'must-not'] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: 'Routes the headless change to claude plugin configure eol-normalizer@ --values-stdin with {"eol_normalizer_enabled": "false"}, then the claude plugin install --config eol_normalizer_enabled=false rerun as the fallback; rules out uninstalling; says -s defaults to user; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' +expected_outcome: 'Routes the headless change to the native configure flow for eol-normalizer@ with eol_normalizer_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' --- /eol-normalizer:setup apply diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/configure-route.json b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/configure-route.json index 386da1b5eab..fd3b2a52dc4 100644 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/configure-route.json +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/configure-route.json @@ -2,28 +2,24 @@ "pass": [ { "answer": "claude plugin configure eol-normalizer@ --values-stdin < values.json" - }, - { - "answer": "Run `claude plugin configure eol-normalizer@melodic-software --values-stdin` with the JSON on stdin.", - "why": "real marketplace, two spaces" } ], "fail": [ { "answer": "claude plugin configure eol-normalizer --values-stdin", - "why": "no marketplace" - }, - { - "answer": "/plugin configure eol-normalizer@m", - "why": "interactive route" + "why": "near miss: no marketplace" }, { "answer": "claude plugin install eol-normalizer@m --config eol_normalizer_enabled=false", - "why": "fallback only" + "why": "an install rerun, not the configure flow" }, { "answer": "", "why": "empty" + }, + { + "answer": "Run `/plugin configure eol-normalizer@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/fresh-session-observed-value.json b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/fresh-session-observed-value.json index 0c407288dc8..5905a29c348 100644 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/fresh-session-observed-value.json +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/fresh-session-observed-value.json @@ -1,7 +1,7 @@ { "pass": [ { - "answer": "Then start a fresh session and rerun `/eol-normalizer:setup check`: a check in this same session still shows the old value, because the option is injected at load. Go by what that check reports." + "answer": "Then start a fresh session and rerun `/eol-normalizer:setup check`: a check in this same session can still show the old value. Go by what that check reports." } ], "fail": [ diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/no-uninstall-with-reason.json b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/no-uninstall-with-reason.json deleted file mode 100644 index 8909e83da7d..00000000000 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall eol-normalizer@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/no-uninstall.json b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/no-uninstall.json new file mode 100644 index 00000000000..6e0d568ba7d --- /dev/null +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall to reconfigure: an uninstall is not a way to change an option. Use the configure route." + }, + { + "answer": "No. Never uninstall to turn the hook off." + } + ], + "fail": [ + { + "answer": "Simplest route: `claude plugin uninstall eol-normalizer@`, then reinstall with the option set.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/values-false.json b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/values-false.json index e800b35a4bc..5a2bf0ba789 100644 --- a/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/values-false.json +++ b/plugins/eol-normalizer/evals/setup-headless-toggle-off-configures-never-uninstalls/samples/values-false.json @@ -4,23 +4,19 @@ "answer": "`{\"eol_normalizer_enabled\": \"false\"}`" }, { - "answer": "{\"eol_normalizer_enabled\":\"false\"}", - "why": "no space" + "answer": "Set eol_normalizer_enabled to false.", + "why": "key and value in prose" } ], "fail": [ - { - "answer": "{\"eol_normalizer_enabled\": false}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "eol_normalizer_enabled=false", - "why": "the --config form, not the JSON" - }, { "answer": "{\"eol_normalizer_enabled\": \"true\"}", "why": "wrong value" }, + { + "answer": "{\"eol_normalizer\": \"false\"}", + "why": "near miss: wrong key" + }, { "answer": "", "why": "empty" diff --git a/plugins/eol-normalizer/reference/plugin-reconfiguration.md b/plugins/eol-normalizer/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/eol-normalizer/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/eol-normalizer/skills/setup/SKILL.md b/plugins/eol-normalizer/skills/setup/SKILL.md index 9872b12762a..12002b3d05e 100644 --- a/plugins/eol-normalizer/skills/setup/SKILL.md +++ b/plugins/eol-normalizer/skills/setup/SKILL.md @@ -20,6 +20,12 @@ shows the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + `git` is probed in the body, not here ([gather-block convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/gather-block/README.md#the-pre-compute-block-runs-as-one-shell-invocation), "The pre-compute block runs as one shell invocation"). @@ -90,38 +96,27 @@ tool, so `apply` installs nothing and writes nothing. It only points: - missing `node` / `jq` / Bash / git: platform install instructions from the README Requirements section; this skill never installs system packages. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention, which owns the verified-version record - (): - interactive `/plugin configure eol-normalizer@` any time. Headless, preferred on - Claude Code 2.1.285 or later: - `claude plugin configure eol-normalizer@ --values-stdin < values.json`, where - `values.json` holds `{"eol_normalizer_enabled": "true"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install eol-normalizer@ -s --config eol_normalizer_enabled=true` - (repeatable per key). Do **not** uninstall to reconfigure: uninstalling drops this plugin's - entire stored `pluginConfigs` entry, resetting every option in the README's Options reference - to its manifest default. `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, `-s` defaults to `user`; pass the scope `claude plugin list` reports for this plugin, and for a - `project` or `local` scope run from that project's directory, so the rerun matches the existing - install record; from the home directory pass `user`. `-s` places the install record and - `enabledPlugins`, and a rerun at another scope adds an install record at that scope and enables - the plugin there. Whichever route ran, read both its exit code and its output before reporting - the value saved: the two routes signal a rejected value differently. This skill never writes user - settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**. The rendered - `${user_config.*}` is injected at skill load and each hook receives its - `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a same-session `check` - still reports the OLD value; report the observed effective value, never an unobserved change. - - - **Pointer**: when checking how a route reports a rejected value, what the rerun prints, the - `-s` default, where the value is stored, or what an uninstall deletes, fetch - [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), - [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install), - [§ Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) - and [§ What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps) - live. **As of**: 2026-11-10. **Recheck trigger**: one of those sections changes how a route - reports a rejection, the rerun's output, the `-s` default, where a value is stored, or what an - uninstall deletes. +- toggle off: set `eol_normalizer_enabled` to `true` through Claude Code's native flow, + interactively with `/plugin configure eol-normalizer@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - no `eol=` policy: this is the opt-out, not a defect. Point at the repository's own `.gitattributes` as the place to declare policy; this skill never writes `.gitattributes`, because that would impose a repo-wide line-ending policy the plugin has no mandate to diff --git a/plugins/firecrawl/skills/setup/SKILL.md b/plugins/firecrawl/skills/setup/SKILL.md index c9ec3f19f8c..37a65112347 100644 --- a/plugins/firecrawl/skills/setup/SKILL.md +++ b/plugins/firecrawl/skills/setup/SKILL.md @@ -18,6 +18,12 @@ the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` diff --git a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/case.yaml b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/case.yaml index 534c486128d..05733dd3b32 100644 --- a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/case.yaml +++ b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/case.yaml @@ -5,27 +5,15 @@ context: graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+go-format@\S+' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+go-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"go_format_enabled"\s*:\s*"false"' + pattern: '\bgo_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+go-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!go-format@)\w+)?)*\s+go-format@\S+[^\n]*?--config[ =]go_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin still writes the value.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says to pass the scope claude plugin list reports for this plugin; that a project or local scope needs the command run from that project''s directory; that a rerun at another scope adds an install record at that scope; and that from the home directory the scope to pass is user.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -64,3 +52,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+go\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!go\-format@)(?:\w+|<[^>\s]+>))?)*\s+go\-format@\S+(?:\\\n|[^\n])*?--config[ =]go_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/prompt.md b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/prompt.md index 3ba1371c6d9..08622c9d475 100644 --- a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/prompt.md +++ b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md toggle bullet (lines 154-185): claude plugin configure --values-stdin with string values is preferred (lines 160-163); the claude plugin install --config rerun is the fallback before 2.1.285 (lines 164-165), and against an installed plugin it still writes the value (lines 155-156); never uninstall, it resets every option (lines 168-169); pass the scope claude plugin list reports, run from that project's directory for a project or local scope, a rerun at another scope adds an install record there, and from the home directory pass user (lines 172-177); a same-session check still reports the old value, so rerun check in a fresh session (lines 178-180). The prompt raises each claim. Dropped: legacy 'cites the marketplace's plugin-reconfiguration convention', since the skill cites it as its own source (line 157) and never tells the reply to name it. Legacy 'ends by rerunning check' is the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself (line 185). Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md toggle bullet: reconfigure through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. configure-route also accepts a flag before the target. Legacy 'ends by rerunning check' is the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [go-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes the headless change to claude plugin configure go-format@ --values-stdin with {\"go_format_enabled\": \"false\"}, then the claude plugin install --config go_format_enabled=false rerun as the fallback that still writes on an installed plugin; rules out uninstalling; gives the scope rule; tells the user to rerun check in a fresh session and go by the observed value; runs no claude plugin write itself." +expected_outcome: "Routes the headless change to the native configure flow for go-format@ with go_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the fallback, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and go by the observed value; runs no claude plugin write itself." --- /go-format:setup apply diff --git a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/configure-route.json b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/configure-route.json index 364edac2a31..3fe8c0df8c6 100644 --- a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/configure-route.json +++ b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/configure-route.json @@ -3,27 +3,23 @@ { "answer": "`claude plugin configure go-format@ --values-stdin < values.json`" }, - { - "answer": "claude plugin configure go-format@melodic-software --values-stdin", - "why": "real marketplace, two spaces" - }, { "answer": "claude plugin configure --values-stdin go-format@melodic-software", - "why": "the flag before the target" + "why": "a flag before the target" } ], "fail": [ { - "answer": "/plugin configure go-format@", - "why": "interactive route" + "answer": "claude plugin install go-format@m --config go_format_enabled=false", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure go-format --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { - "answer": "claude plugin configure go-format@", - "why": "near miss: no --values-stdin" + "answer": "Run `/plugin configure go-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/no-uninstall-with-reason.json b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/no-uninstall-with-reason.json deleted file mode 100644 index 55b1556d03a..00000000000 --- a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Do not uninstall to reconfigure: uninstalling drops this plugin's stored pluginConfigs entry and resets every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Uninstall and reinstall is fine.", - "why": "tells the user to uninstall" - }, - { - "answer": "Please do not uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/no-uninstall.json b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/no-uninstall.json new file mode 100644 index 00000000000..761713b04fc --- /dev/null +++ b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Uninstall then reinstall with --config is the simplest route.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/values-false.json b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/values-false.json index f09210757aa..3bebd8d20d4 100644 --- a/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/values-false.json +++ b/plugins/go-format/evals/su-apply-headless-toggle-off-configures/samples/values-false.json @@ -4,22 +4,18 @@ "answer": "`{\"go_format_enabled\": \"false\"}`" }, { - "answer": "{\"go_format_enabled\":\"false\"}", - "why": "no space" + "answer": "Set go_format_enabled to false.", + "why": "key and value in prose" } ], "fail": [ - { - "answer": "{\"go_format_enabled\": false}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "go_format_enabled=false", - "why": "the --config form, not the JSON" - }, { "answer": "{\"go_format_enabled\": \"true\"}", "why": "wrong value" + }, + { + "answer": "{\"go_format\": \"false\"}", + "why": "near miss: wrong key" } ] } diff --git a/plugins/go-format/evals/su-check-gitignored-option-default/case.yaml b/plugins/go-format/evals/su-check-gitignored-option-default/case.yaml index d58f7fbd162..99a28fc1df5 100644 --- a/plugins/go-format/evals/su-check-gitignored-option-default/case.yaml +++ b/plugins/go-format/evals/su-check-gitignored-option-default/case.yaml @@ -23,7 +23,7 @@ graders: focus: 'last_message' - name: 'opt-in-route' type: 'llm' - criteria: 'The reply names setting go_format_lint_gitignored to true as the way to act on gitignored files, through the plugin''s own settings flow: /plugin configure, claude plugin configure --values-stdin or a claude plugin install --config rerun.' + criteria: 'The reply names setting go_format_lint_gitignored to true as the way to act on gitignored files, through the plugin''s own settings flow: Claude Code''s native plugin configure flow, interactive or headless.' focus: 'last_message' - name: 'no-repo-config-needed' type: 'llm' diff --git a/plugins/go-format/reference/plugin-reconfiguration.md b/plugins/go-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/go-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/go-format/skills/setup/SKILL.md b/plugins/go-format/skills/setup/SKILL.md index 3ef0ff32d46..b0c4b10d40e 100644 --- a/plugins/go-format/skills/setup/SKILL.md +++ b/plugins/go-format/skills/setup/SKILL.md @@ -26,6 +26,12 @@ tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -158,38 +164,27 @@ re-verifying. For everything else `apply` only points: toolchain: https://go.dev/dl/). - missing `node` / `jq` / Bash: platform install instructions from the README Requirements section; this skill never installs system packages. -- toggle off: the marketplace's plugin-reconfiguration convention owns the routes, the caveats, - the measured CLI behavior (including that a rerun against an already-installed plugin still - writes the value) and its verification record - (). - Consumer-run routes, `go_format_lint_gitignored` set the same way: - - Interactive: `/plugin configure go-format@`. - - Headless, preferred on Claude Code 2.1.285 or later: - `claude plugin configure go-format@ --values-stdin < values.json`, where - `values.json` holds `{"go_format_enabled": "false"}` (every value a string, booleans included; - keys left out keep their saved values). - - Headless fallback, before 2.1.285: - `claude plugin install go-format@ -s --config go_format_enabled=false`. - - Print these four caveats with them: - - Never uninstall to reconfigure: it drops this plugin's entire stored `pluginConfigs` entry and - resets every option to its manifest default. - - Scope. The option value always lands in user settings, whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is - installed in. For the rerun, pass the scope `claude plugin list` reports for this plugin, and - for a `project` or `local` scope run from that project's directory, so the rerun matches the - existing install record. `-s` places the install record and `enabledPlugins`, and a rerun at - another scope adds an install record at that scope and enables the plugin there. When the - working directory is the home directory, project scope and user scope are the same settings - file, so the list can label that one file as both `user` and `project`: pass `user`. - - Observation is next-session: a same-session `check` still reports the OLD value, so rerun - `check` in a **fresh session** and report the observed effective value, never an unobserved - change. - - Check each route's own failure signal: `--values-stdin` rejects a bad value with exit 1 and - saves nothing, but stores an empty string as `""`; the `--config` rerun prints a warning for a - rejected value yet exits 0, so read its output, not its exit code. - - This skill never writes user settings or `pluginConfigs`. +- toggle off: set `go_format_enabled` to `false` (and `go_format_lint_gitignored` the same way) + through Claude Code's native flow, interactively with `/plugin configure go-format@` + or by a headless route, as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. Re-running `apply` after everything passes changes nothing and reports "already configured". diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/case.yaml b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/case.yaml index ca41a2078bc..7494cf41893 100644 --- a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/case.yaml +++ b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/case.yaml @@ -11,27 +11,15 @@ graders: arm: 'with-only' - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure guardrails@\S+\s+--values-stdin' + pattern: 'claude plugin configure guardrails@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '\"block_cd_prefix_enabled\"\s*:\s*\"false\"' + pattern: '\bblock_cd_prefix_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure guardrails@\S+\s+--values-stdin[\s\S]*?claude plugin install guardrails@\S+[^\n]*?--config[ =]block_cd_prefix_enabled=false\b' - target: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry, resetting every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' - type: 'llm' - criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install rerun the scope claude plugin list reports for this plugin.' - focus: 'last_message' - - name: 'failure-signals' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says --values-stdin rejects a bad value with exit 1 and saves nothing, while the --config rerun exits 0 even on a rejected value, so its output has to be read.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -81,3 +69,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+guardrails@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!guardrails@)(?:\w+|<[^>\s]+>))?)*\s+guardrails@\S+(?:\\\n|[^\n])*?--config[ =]block_cd_prefix_enabled=false\b' + target: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' + - name: 'failure-signals' + type: 'llm' + criteria: 'The reply tells the user to check each route''s own failure signal, reading both its exit code and its output before treating a value as saved, because the two routes do not report a rejected value the same way.' + focus: 'last_message' diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/matchers.json b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/matchers.json index 9793f82a26e..975d2e52362 100644 --- a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/matchers.json +++ b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/matchers.json @@ -6,13 +6,10 @@ "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?node(?:\\.exe)?(?:\\\\\\\"|')?(?:\\s|\\\\\\\\\\\\n)+(?:-[^\\s\\\"\\\\]+(?:\\s|\\\\\\\\\\\\n)+)*(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\']|\\\\\\\\)*install\\.mjs(?:\\\\\\\"|')?(?:\\s|\\\\\\\\\\\\n)+verify\\b" ], "configure-route": [ - "claude plugin configure guardrails@\\S+\\s+--values-stdin" + "claude plugin configure guardrails@\\S+" ], "values-false": [ - "\\\"block_cd_prefix_enabled\\\"\\s*:\\s*\\\"false\\\"" - ], - "fallback-after-configure": [ - "claude plugin configure guardrails@\\S+\\s+--values-stdin[\\s\\S]*?claude plugin install guardrails@\\S+[^\\n]*?--config[ =]block_cd_prefix_enabled=false\\b" + "\\bblock_cd_prefix_enabled\\b[^\\n]{0,40}?\\bfalse\\b" ], "names-install-git-hooks": [ "\\bapply install-git-hooks(?![\\w-]|\\.\\w)" diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/prompt.md b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/prompt.md index bf5a5e677d3..a7e67caaac8 100644 --- a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/prompt.md +++ b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/prompt.md @@ -1,10 +1,10 @@ --- -description: 'Legacy apply-points-only-never-writes, with legacy install-git-hooks-is-explicit-only merged in (same /guardrails:setup apply prompt). SKILL.md apply (lines 106-137): configure --values-stdin with every value a string is preferred (lines 117-121); the install --config rerun is the fallback and still writes on an installed plugin (lines 121-124); never uninstall, which resets every option (lines 124-126); configure has no scope flag, so run it from the project, and the rerun takes the scope claude plugin list reports (lines 127-131); --values-stdin exits 1 on a bad value while --config exits 0, so read its output (lines 131-134); rerun check in a fresh session (lines 135-137). install-git-hooks never runs from bare apply (line 162). verify-ran shows check ran first (line 108). Dropped: ''cites the plugin-reconfiguration convention as the owner of the verified-version record'' (the skill cites it as its own source and never tells the reply to name it), and ''never installs system packages'' is no-install. Host tools: jq 1.6+, shfmt 3.14.1+, node, bash 5+ and git; without jq or shfmt the with-plugin arm''s own guards deny every Bash call. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail.' +description: 'Legacy apply-points-only-never-writes, with legacy install-git-hooks-is-explicit-only merged in (same /guardrails:setup apply prompt). SKILL.md apply: reconfigure through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, scope-rule and failure-signals grade them there. install-git-hooks never runs from bare apply. verify-ran shows check ran first. ''never installs system packages'' is no-install. Host tools: jq 1.6+, shfmt 3.14.1+, node, bash 5+ and git; without jq or shfmt the with-plugin arm''s own guards deny every Bash call. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail.' tags: [guardrails, setup, apply, reconfigure, must-not, no-network] max_turns: 16 timeout_seconds: 420 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: 'Runs check, then routes to claude plugin configure guardrails@ --values-stdin with {"block_cd_prefix_enabled": "false"}, then the install --config rerun; rules out uninstalling with its reason; gives the scope rule, both failure signals and the fresh-session rerun; says bare apply installs no git hook and apply install-git-hooks does; installs, writes and runs no plugin CLI write itself.' +expected_outcome: 'Runs check, then routes to the native configure flow for guardrails@ with block_cd_prefix_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; gives the fresh-session rerun; says bare apply installs no git hook and apply install-git-hooks does; installs, writes and runs no plugin CLI write itself.' --- /guardrails:setup apply diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/configure-route.json b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/configure-route.json index fb86d04831b..f1a40a1ba75 100644 --- a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/configure-route.json +++ b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/configure-route.json @@ -2,23 +2,20 @@ "pass": [ { "answer": "claude plugin configure guardrails@melodic-software --values-stdin < values.json" - }, - { - "answer": "Run `claude plugin configure guardrails@ --values-stdin < values.json`." } ], "fail": [ { "answer": "claude plugin install guardrails@m --config block_cd_prefix_enabled=false", - "why": "only the fallback" + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure guardrails --values-stdin", "why": "near miss: no marketplace" }, { - "answer": "Use /plugin configure guardrails@m.", - "why": "near miss: interactive route" + "answer": "Run `/plugin configure guardrails@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/no-uninstall-with-reason.json b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/no-uninstall-with-reason.json deleted file mode 100644 index c08b6ae7c8b..00000000000 --- a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: that drops the plugin's whole pluginConfigs entry, resetting every option to its manifest default." - }, - { - "answer": "No. An uninstall wipes the stored options, so every guard setting returns to its default." - } - ], - "fail": [ - { - "answer": "Uninstall then reinstall with --config is the simplest route.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - }, - { - "answer": "Don't uninstall: it only resets block_cd_prefix_enabled.", - "why": "near miss: understates the reset" - } - ] -} diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/no-uninstall.json b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/no-uninstall.json new file mode 100644 index 00000000000..761713b04fc --- /dev/null +++ b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Uninstall then reinstall with --config is the simplest route.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/values-false.json b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/values-false.json index 6468946db45..6a331a9d696 100644 --- a/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/values-false.json +++ b/plugins/guardrails/evals/su-apply-headless-toggle-and-no-hooks/samples/values-false.json @@ -4,13 +4,13 @@ "answer": "{\"block_cd_prefix_enabled\": \"false\"}" }, { - "answer": "```json\n{ \"block_cd_prefix_enabled\":\"false\" }\n```" + "answer": "Set block_cd_prefix_enabled to false." } ], "fail": [ { - "answer": "{\"block_cd_prefix_enabled\": false}", - "why": "near miss: a boolean, not a string" + "answer": "{\"block_cd_prefix_enabled\": \"true\"}", + "why": "wrong value" }, { "answer": "{\"block_cd_prefix\": \"false\"}", diff --git a/plugins/guardrails/reference/plugin-reconfiguration.md b/plugins/guardrails/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/guardrails/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/guardrails/skills/setup/SKILL.md b/plugins/guardrails/skills/setup/SKILL.md index 276b0b48471..b983dc5c572 100644 --- a/plugins/guardrails/skills/setup/SKILL.md +++ b/plugins/guardrails/skills/setup/SKILL.md @@ -26,6 +26,12 @@ each shows the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -114,30 +120,27 @@ the retired-hook cleanup below, writes nothing. It points: - missing `jq` or `node` / old Bash / missing or old `shfmt`: install instructions from the README Requirements section; this skill never installs system packages. -- any toggle or scalar change: reconfigure through Claude Code's native flow, per the - marketplace's plugin-reconfiguration convention - (, - which owns the verified-version record). Interactive: - `/plugin configure guardrails@` any time. Headless, preferred on Claude Code - 2.1.285 or later: `claude plugin configure guardrails@ --values-stdin < values.json`, - where `values.json` holds `{"KEY": "VALUE"}` (every value a string, booleans included; keys left - out keep their saved values). Headless fallback, before 2.1.285: - `claude plugin install guardrails@ -s --config KEY=VALUE` (repeatable per - key); against an already-installed plugin it prints `already installed` and still writes the - value. Do **not** uninstall to reconfigure: that drops the plugin's entire stored - `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. The value always lands in user settings, whichever route writes it. - `claude plugin configure` has no scope flag, so run it from the project the plugin is installed - in. For the rerun, pass the scope `claude plugin list` reports for this plugin: `-s` places the - install record and `enabledPlugins`, and a rerun at another scope adds an install record there - and enables the plugin. From the home directory the list can label one file both `user` and - `project`: pass `user`. Check each route's own failure signal: `--values-stdin` rejects a bad - value with exit 1 and saves nothing, but stores an empty string as `""`; the `--config` rerun - prints a warning for a rejected value yet exits 0, so read its output, not the exit code. This - skill never writes user settings or `pluginConfigs`. - Afterwards rerun `check` in a **fresh session**: the rendered `${user_config.*}` and each - hook's `CLAUDE_PLUGIN_OPTION_*` are fixed at session start, so a same-session `check` still - reports the OLD value; report the observed effective value, never an unobserved change. +- any toggle or scalar change: reconfigure through Claude Code's native flow, interactively with + `/plugin configure guardrails@` or by a headless route, as the marketplace's + plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. **Retired hooks.** After the points above, re-run the retirement detection from `check` step 8; per finding, individually gated: `delete` → confirm, then run diff --git a/plugins/harness-config/skills/setup/SKILL.md b/plugins/harness-config/skills/setup/SKILL.md index a059699e5fd..5fea2c7508d 100644 --- a/plugins/harness-config/skills/setup/SKILL.md +++ b/plugins/harness-config/skills/setup/SKILL.md @@ -25,6 +25,12 @@ tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Setup per the uniform setup contract (`docs/plugin-philosophy.md` "Setup is explicit and repeatable" in diff --git a/plugins/harness-ops/reference/plugin-reconfiguration.md b/plugins/harness-ops/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/harness-ops/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/harness-ops/skills/setup/SKILL.md b/plugins/harness-ops/skills/setup/SKILL.md index 0b1ffb9a0e2..a9e2d27ed25 100644 --- a/plugins/harness-ops/skills/setup/SKILL.md +++ b/plugins/harness-ops/skills/setup/SKILL.md @@ -179,36 +179,31 @@ The options live in Claude Code's native plugin configuration, which setup must plugin's hooks module hooks no event, so the log starts no process. Turning it on makes the module start one process per observable hook event plus `SessionEnd` retention, and only where mods load; the README's per-session hook event log section carries the measured cost and where it does not run. -- **Reconfiguring a personal option:** through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure harness-ops@` - any time. Headless, preferred on Claude Code 2.1.285 or later: - `claude plugin configure harness-ops@ --values-stdin < values.json`, where - `values.json` holds `{"registry_dir": ""}` (every value a string, - `"session_event_log_enabled": "true"` included; keys left out keep their saved values). Headless - fallback, before 2.1.285: `claude plugin install harness-ops@ -s --config - registry_dir=` (repeatable per key); against an already-installed plugin it prints - `already installed` **and still writes the value**. Do **not** uninstall to reconfigure: that - drops this plugin's entire stored `pluginConfigs` entry, resetting every option in the README's - Options reference (the audit toggles included) to its manifest default. The value always lands - in user settings, whichever route writes it. `claude plugin configure` has no scope flag, so run - it from the project the plugin is installed in. For the rerun, `-s` defaults to `user`; pass the - scope `claude plugin list` reports for this plugin (from the home directory pass `user`), and run from that project's directory for a - `project`/`local` scope, or the rerun adds a second install record at the scope passed and - enables the plugin there. Check each route's own failure signal: `--values-stdin` rejects a bad - value with exit 1 and saves nothing, but stores an empty string as `""`; a rejected `--config` - value prints a warning and still exits 0, so read the command's output rather than its exit - code. This skill never writes user settings or - `pluginConfigs`. Afterwards rerun - `check` in a **fresh session**: the rendered `${user_config.*}` is injected at skill load and each - hook receives its `CLAUDE_PLUGIN_OPTION_*` from an environment fixed at session start, so a - same-session `check` still reports the OLD value; report the observed effective value, never an - unobserved change. +- **Reconfiguring a personal option** (`registry_dir`, `session_event_log_enabled` and the rest): + through Claude Code's native flow, interactively with `/plugin configure harness-ops@` + or by a headless route, as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. After any reconfiguration, rerun `check` in a **fresh session** and report every observed effective destination, never claim an unobserved change, and never read a same-session `check` still showing -the old value as a failed write (see the reconfiguration note above for why it does). Re-running +the old value as a failed write. Re-running `check` or `apply` when every destination is contained (or defaulted) and the guard is in place changes nothing and reports "already configured". diff --git a/plugins/knowledge/reference/plugin-reconfiguration.md b/plugins/knowledge/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/knowledge/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/knowledge/skills/setup/SKILL.md b/plugins/knowledge/skills/setup/SKILL.md index 1d585198574..29297c2e2b6 100644 --- a/plugins/knowledge/skills/setup/SKILL.md +++ b/plugins/knowledge/skills/setup/SKILL.md @@ -72,28 +72,29 @@ Run `check`, then resolve each finding. Re-running after everything passes chang reports "already configured". 1. **`library_dir` mismatch. Guidance only.** `library_dir` is a personal `userConfig` scalar; - never hand-edit `pluginConfigs` or write Claude Code settings. Reconfigure through Claude - Code's native flow, per the marketplace's plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure knowledge@` - any time. Headless, preferred: run - `claude plugin configure knowledge@ --values-stdin < values.json` from the project - the plugin is installed in, with `values.json` holding `{"library_dir": ""}` (every - value a string; keys left out keep their saved values). Headless fallback, for a Claude Code - release without `plugin configure`: rerun - `claude plugin install knowledge@ -s --config library_dir=` - (repeatable per key). Against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops the plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. Either route stores the value in user settings. `claude plugin configure` - has no scope flag and finds only an install visible from the working directory. For the rerun, - `-s` defaults to `user`; pass the scope `claude plugin list` reports, and run from that - project's directory for a `project`/`local` scope, or the rerun adds a second install record at - the scope passed and enables the plugin there; from the home directory pass `user`. Check each - route's own failure signal: `claude plugin configure` rejects a bad value with exit 1 and saves - nothing, but stores an empty string as `""`; the rerun prints a warning for a rejected value yet - exits 0, so read its output. Afterwards rerun `check` in a **fresh session** and report the - observed effective value, never an unobserved change. For where `${user_config.*}` resolves, see + never hand-edit `pluginConfigs` or write Claude Code settings. Reconfigure `library_dir` + through Claude Code's native flow, interactively with + `/plugin configure knowledge@` or by a headless route, as the marketplace's + plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. Afterwards rerun + `check` in a **fresh session**, since a same-session `check` can still show the old value, and + report the observed effective value, never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. + + For where `${user_config.*}` resolves, see [Plugin manifest reference: Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value) (as of 2026-11-04; recheck when that section changes when skill content takes a value). For a root outside the project and home directories, recommend the portable value forms from the diff --git a/plugins/knowledge/skills/setup/evals/evals.json b/plugins/knowledge/skills/setup/evals/evals.json index 095affdb2e4..4272276862a 100644 --- a/plugins/knowledge/skills/setup/evals/evals.json +++ b/plugins/knowledge/skills/setup/evals/evals.json @@ -17,12 +17,12 @@ "id": 2, "name": "apply-mismatch-is-guidance-only", "prompt": "Run /knowledge:setup apply where the observed library_dir is a machine-absolute path that contradicts the repository convention. Fix it.", - "expected_output": "Routes the fix to Claude Code's own plugin configuration commands — /plugin configure knowledge interactively, or headless claude plugin configure knowledge@ --values-stdin with library_dir in a JSON object, falling back to a plain claude plugin install ... --config library_dir= rerun at the scope claude plugin list reports — and rules out uninstalling to reconfigure; it never hand-edits pluginConfigs or Claude Code settings, and defers verification of the new value to a fresh session.", + "expected_output": "Routes the fix to Claude Code's native configure flow for library_dir — /plugin configure knowledge@ interactively, or a headless route from the marketplace's plugin-reconfiguration convention — and rules out uninstalling to reconfigure; it never hand-edits pluginConfigs or Claude Code settings, and defers verification of the new value to a fresh session.", "expectations": [ "Does not write library_dir into any settings or pluginConfigs file", - "Headless path names `claude plugin configure knowledge@ --values-stdin` first, run from the project the plugin is installed in, and the `claude plugin install ... --config` rerun second as the fallback for a release without `plugin configure`", - "For the rerun fallback, it cites the marketplace's plugin-reconfiguration convention as the owner of the verified-version record and stating the scope rule (`-s` defaults to `user`; pass the scope `claude plugin list` reports)", - "Never prescribes `claude plugin uninstall` as the way to reconfigure, and gives the reason: uninstalling drops the whole stored pluginConfigs entry, resetting every option to its manifest default", + "Names the native configure flow for knowledge@ (`/plugin configure` or `claude plugin configure`) as the way to change library_dir", + "Points at the marketplace's plugin-reconfiguration convention for the headless routes and their scope and failure-signal caveats, rather than restating them", + "Never prescribes `claude plugin uninstall` as the way to reconfigure", "Recommends a portable value form (~ prefix or env-var reference) over a literal machine path", "Does not claim the changed value is in effect this session" ] diff --git a/plugins/machine-health/evals/setup-report-dir-plugin-configure/prompt.md b/plugins/machine-health/evals/setup-report-dir-plugin-configure/prompt.md index 652b7cebace..f3840ead249 100644 --- a/plugins/machine-health/evals/setup-report-dir-plugin-configure/prompt.md +++ b/plugins/machine-health/evals/setup-report-dir-plugin-configure/prompt.md @@ -1,10 +1,10 @@ --- -description: "Asked to move the report folder, setup treats it as the report_dir plugin option stored in plugin config, not catalog overlay state: it points at /plugin configure machine-health@ or the headless claude plugin configure route, never writes it into checks.local.jsonc, and says a fresh session is needed to see the new value (setup/SKILL.md interview step 6, 'What this skill does NOT do'). Narration: the prompt gives the check results as facts and the case grades the proposal. names-plugin-configure, names-report-dir-option and fresh-session-recheck grade terms the plugin holds and the prompt lacks; not-in-overlay-json fails an overlay entry carrying the path; plugin-option-not-overlay judges the rest. A values.json holding report_dir is the documented headless route and passes. Slash-invoked (disable-model-invocation), so there is no Skill grader." +description: "Asked to move the report folder, setup treats it as the report_dir plugin option stored in plugin config, not catalog overlay state: it points at /plugin configure machine-health@ or the headless claude plugin configure route, never writes it into checks.local.jsonc, and says a fresh session is needed to see the new value (setup/SKILL.md interview step 6, 'What this skill does NOT do'). Narration: the prompt gives the check results as facts and the case grades the proposal. names-plugin-configure, names-report-dir-option and fresh-session-recheck grade terms the plugin holds and the prompt lacks; not-in-overlay-json fails an overlay entry carrying the path; plugin-option-not-overlay judges the rest. A headless route from the plugin-reconfiguration convention the skill cites, a values file holding report_dir included, passes. Slash-invoked (disable-model-invocation), so there is no Skill grader." tags: [machine-health, setup, apply, plugin-config, narration, invented] max_turns: 10 timeout_seconds: 300 allowed_tools: [Skill, Read, Glob, Grep] -expected_outcome: "The reply says the report folder is the report_dir plugin option, changed through /plugin configure machine-health@ (or claude plugin configure machine-health@ --values-stdin with {\"report_dir\": \"D:\\\\Reports\\\\Health\"}), not through the catalog overlay, and that a fresh session shows the new value. It writes nothing into checks.local.jsonc or any file." +expected_outcome: "The reply says the report folder is the report_dir plugin option, changed through /plugin configure machine-health@ (or a headless route from the plugin-reconfiguration convention) with report_dir set to D:\\Reports\\Health, not through the catalog overlay, and that a fresh session shows the new value. It writes nothing into checks.local.jsonc or any file." --- /machine-health:setup apply diff --git a/plugins/machine-health/reference/plugin-reconfiguration.md b/plugins/machine-health/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/machine-health/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/machine-health/skills/setup/SKILL.md b/plugins/machine-health/skills/setup/SKILL.md index 2d69068fca3..e2dc1e67f66 100644 --- a/plugins/machine-health/skills/setup/SKILL.md +++ b/plugins/machine-health/skills/setup/SKILL.md @@ -255,45 +255,31 @@ Reject an argument targeting an unknown check id, remediation or muted key with `Set-ApprovalState -Action approve` (above). Never enable a remediation the user did not explicitly approve. 6. **Confirm the report directory.** Show where reports land (the `report_dir` plugin option when set, else `$env:USERPROFILE\Documents\MachineHealth`). The option is stored in plugin config, - not the overlay; reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure machine-health@` - any time. `` is the owner's installed marketplace name: fill it in only when the + not the overlay; reconfigure `report_dir` through Claude Code's native flow, interactively with + `/plugin configure machine-health@` or by a headless route, as the marketplace's + plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + `` is the owner's installed marketplace name: fill it in only when the owner gave it in this conversation, else hand the commands over with `` as written. - Never search, list or read files (manifests, the plugin cache, settings) to find it. Headless, preferred: run - `claude plugin configure machine-health@ --values-stdin < values.json` from the - project the plugin is installed in, with `values.json` holding `{"report_dir": ""}` - (every value a string; keys left out keep their saved values; write a Windows path with `\\` - or forward slashes, since a bare `\` is a JSON escape). PowerShell reserves `<`, so there pipe - the file instead: `Get-Content values.json | claude plugin configure machine-health@ --values-stdin`. - Headless fallback, for a Claude - Code release without `plugin configure`: rerun - `claude plugin install machine-health@ -s --config report_dir=` - (repeatable per key). Against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops the plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. `claude plugin configure` - has no scope flag and finds only an install visible from the working directory. For the rerun, - `-s` defaults to `user`; pass the scope `claude plugin list` reports, and run from that - project's directory for a `project`/`local` scope, or the rerun adds a second install record at - the scope passed and enables the plugin there; from the home directory pass `user`. Check each - route's own failure signal: `claude plugin configure` rejects a bad value with exit 1 and saves - nothing, but stores an empty string as `""`; the rerun prints a warning for a rejected value yet - exits 0, so read its output. - - **Pointer**: for how each route resolves the install, its scope default, and what it prints - and exits with on a rejected value, fetch - [Plugin commands reference: plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) - and [plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + Never search, list or read files (manifests, the plugin cache, settings) to find it. Where a + route takes the value as JSON, write a Windows path with `\\` or forward slashes, since a bare + `\` is a JSON escape. Never uninstall to reconfigure; that section links what an uninstall + deletes. This skill never writes user settings or `pluginConfigs`. Afterwards rerun `check` in a + **fresh session**, since a same-session `check` can still show the old value, and report the + observed effective value, never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) live. - - **As of**: 2026-11-03 - - **Recheck trigger**: either section changes how it finds an install, its scope default, or - its output or exit code on a rejected value. - - This skill never writes user settings or `pluginConfigs`. Afterwards rerun - `check` in a **fresh session**. The rendered `${user_config.*}` and each hook's - `CLAUDE_PLUGIN_OPTION_*` are fixed at session start, so a same-session `check` still reports - the OLD value; report the observed effective value, never an unobserved change. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. Re-running `apply` after everything is already set changes nothing and reports "already configured". diff --git a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/case.yaml b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/case.yaml index 20ebccb9e81..b5c6f9d7a0b 100644 --- a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/case.yaml +++ b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/case.yaml @@ -5,31 +5,15 @@ context: graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+markdown-format@\S+' + pattern: 'claude plugin configure markdown-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '\"markdown_format_enabled\"\s*:\s*\"false\"' + pattern: '\bmarkdown_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+markdown-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!markdown-format@)\w+)?)*\s+markdown-format@\S+(?:\\\n|[^\n])*?--config[ =]markdown_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'repeatable-per-key' - type: 'llm' - criteria: 'The reply says the --config flag repeats per key, so the same install rerun can also set markdown_format_lint_gitignored.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says to pass the scope claude plugin list reports for this plugin on the install rerun, and that from the home directory that scope is user.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -68,3 +52,19 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+markdown\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!markdown\-format@)(?:\w+|<[^>\s]+>))?)*\s+markdown\-format@\S+(?:\\\n|[^\n])*?--config[ =]markdown_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' + - name: 'repeatable-per-key' + type: 'llm' + criteria: 'The reply says the --config flag repeats per key, so the same install rerun can also set markdown_format_lint_gitignored.' + focus: 'last_message' diff --git a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/prompt.md b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/prompt.md index a2341052f90..634b6a96e00 100644 --- a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/prompt.md +++ b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 189-213): claude plugin configure --values-stdin with every value a string is preferred (lines 193-197; the skill's example value is true, and the prompt asks for off, so the graders want false); the claude plugin install --config rerun is the fallback, repeatable per key, and against an installed plugin it prints already installed and still writes the value (lines 197-201); never uninstall, it drops the stored pluginConfigs entry and resets every option (lines 201-203); pass the scope claude plugin list reports, and from the home directory user (lines 204-207); a same-session check still reports the old value, so rerun check in a fresh session and report the observed value (lines 210-213). The prompt raises each claim. Legacy 'cites the plugin-reconfiguration convention as the owner of the verified-version record' is dropped: the skill cites it as its own source and never tells the reply to name it. Legacy 'ends by rerunning check' is now the instruction to rerun in a fresh session (lines 210-213): a same-session check cannot observe the change. no-plugin-cli-write guards a write the run makes itself (line 209). The scaffold declines the entry. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: reconfigure through the native configure flow (the skill's example value is true, and the prompt asks for off, so values-false wants false), through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session and report the observed value. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes, repeatable-per-key and scope-rule grade them there. Legacy 'ends by rerunning check' is now the instruction to rerun in a fresh session. no-plugin-cli-write guards a write the run makes itself. The scaffold declines the entry. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [markdown-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes the headless change to claude plugin configure markdown-format@ --values-stdin with {\"markdown_format_enabled\": \"false\"}, then the claude plugin install --config markdown_format_enabled=false rerun as the fallback that repeats per key and still writes on an installed plugin; rules out uninstalling with the reason; passes the scope claude plugin list reports, user from the home directory; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." +expected_outcome: "Routes the change to the native configure flow for markdown-format@ with markdown_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the fallback, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." --- /markdown-format:setup apply diff --git a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/configure-route.json b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/configure-route.json index 5d0343070fa..48a8417bcd5 100644 --- a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/configure-route.json +++ b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/configure-route.json @@ -2,24 +2,20 @@ "pass": [ { "answer": "Run `claude plugin configure markdown-format@ --values-stdin < values.json`." - }, - { - "answer": "`claude plugin configure --debug markdown-format@m --values-stdin`", - "why": "a flag before the target" } ], "fail": [ { - "answer": "Run `claude plugin configure markdown-format@`.", - "why": "interactive form" + "answer": "Run `claude plugin install markdown-format@ --config markdown_format_enabled=false`.", + "why": "an install rerun, not the configure flow" }, { - "answer": "Run `/plugin configure markdown-format@`.", - "why": "the slash command" + "answer": "Run `claude plugin configure markdown-format --values-stdin`.", + "why": "near miss: no marketplace" }, { - "answer": "Run `claude plugin install markdown-format@ --config markdown_format_enabled=false`.", - "why": "only the fallback" + "answer": "Run `/plugin configure markdown-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/no-uninstall-with-reason.json b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/no-uninstall-with-reason.json deleted file mode 100644 index c5b8e211d05..00000000000 --- a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Do not uninstall to reconfigure: that drops the plugin's stored pluginConfigs entry and resets every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Uninstalling and reinstalling works and gives a clean state.", - "why": "allows it" - }, - { - "answer": "Do not uninstall; it is unnecessary here.", - "why": "no reason" - } - ] -} diff --git a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/no-uninstall.json b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/no-uninstall.json new file mode 100644 index 00000000000..54769710cb3 --- /dev/null +++ b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Uninstalling and reinstalling works and gives a clean state.", + "why": "allows it" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/values-false.json b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/values-false.json index 3150158aa37..4ccba783073 100644 --- a/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/values-false.json +++ b/plugins/markdown-format/evals/su-apply-ci-toggle-off-keeps-options/samples/values-false.json @@ -2,20 +2,19 @@ "pass": [ { "answer": "`values.json` holds `{\"markdown_format_enabled\": \"false\"}`." + }, + { + "answer": "Set markdown_format_enabled to false." } ], "fail": [ - { - "answer": "`{\"markdown_format_enabled\": false}`", - "why": "not a string" - }, { "answer": "`{\"markdown_format_enabled\": \"true\"}`", "why": "wrong value" }, { - "answer": "Set markdown_format_enabled to false.", - "why": "no values shape" + "answer": "`{\"markdown_format\": \"false\"}`", + "why": "near miss: wrong key" } ] } diff --git a/plugins/markdown-format/reference/plugin-reconfiguration.md b/plugins/markdown-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/markdown-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/markdown-format/skills/setup/SKILL.md b/plugins/markdown-format/skills/setup/SKILL.md index 97e6e79d6eb..bbf5d3dd060 100644 --- a/plugins/markdown-format/skills/setup/SKILL.md +++ b/plugins/markdown-format/skills/setup/SKILL.md @@ -22,6 +22,12 @@ the tool's path when present, or `absent` when missing: A row missing, reading as literal command text, or reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -194,31 +200,27 @@ install command's exit code alone. For everything else `apply` only points: - missing `jq` / Bash / Node.js: platform install instructions from the README Requirements section; this skill never installs system packages. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure markdown-format@` - any time. Headless, preferred: run - `claude plugin configure markdown-format@ --values-stdin < values.json` from the - project the plugin is installed in, with `values.json` holding - `{"markdown_format_enabled": "true"}` (every value a string, booleans included; keys left out - keep their saved values). Headless fallback, for a Claude Code release without - `plugin configure`: rerun - `claude plugin install markdown-format@ -s --config markdown_format_enabled=true` - (repeatable per key). Against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops the plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. Either route stores the value in user settings. `claude plugin configure` has - no scope flag and finds only an install visible from the working directory. For the rerun, pass - the scope `claude plugin list` reports for this plugin, and for a `project` or `local` scope run - from that project's directory, so the rerun matches the existing install record; from the home - directory pass `user`. Check each route's own failure signal: `claude plugin configure` rejects a - bad value with exit 1 and saves nothing, but stores an empty string as `""`; the rerun prints a - warning for a rejected value yet exits 0, so read its output. This skill never writes user - settings or `pluginConfigs`. Afterwards rerun - `check` in a **fresh session**. The rendered `${user_config.*}` and the hook's - `CLAUDE_PLUGIN_OPTION_*` are fixed at session start, so a same-session `check` still reports - the OLD value; report the observed effective value, never an unobserved change. +- toggle off: set `markdown_format_enabled` to `true` through Claude Code's native flow, + interactively with `/plugin configure markdown-format@` or by a headless route, as + the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - no markdownlint config: this is why the plugin does nothing here, so lead with it rather than leaving it as a footnote under the passing prerequisites. Then offer to create a minimal `.markdownlint-cli2.jsonc` in the repository root only when explicitly asked. diff --git a/plugins/miro/reference/plugin-reconfiguration.md b/plugins/miro/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/miro/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/miro/skills/setup/SKILL.md b/plugins/miro/skills/setup/SKILL.md index 6548c324ad3..8bd4df13621 100644 --- a/plugins/miro/skills/setup/SKILL.md +++ b/plugins/miro/skills/setup/SKILL.md @@ -111,26 +111,30 @@ successful and delivers no tools. time) is the recommended rotation path regardless. It masks input, unlike anything passed on the command line (see the security note below). -Headless, per the marketplace's plugin-reconfiguration convention -(, -which owns the verified-version record), the preferred route is -`claude plugin configure miro@ --values-stdin < values.json`, run from the project the -plugin is installed in, with `values.json` holding `{"miro_api_token": ""}`. It accepts a -`sensitive` option and keeps the token off the command line; delete `values.json` afterwards, or -pipe the object in from your secrets manager. It has no scope flag, finds only an install visible -from the working directory, and rejects a bad value with exit 1 and saves nothing, but stores an -empty string as `""`. The fallback, for a Claude Code release without `plugin configure`, is a -`claude plugin install … -s --config` rerun at the scope `claude plugin list` reports -(`user` from the home directory; another scope adds an install record there): against an -already-installed plugin it prints `already installed` and still writes the value, and a rejected -value prints a warning yet exits 0, so read its output. That record covers only a non-sensitive -option, so do not rely on the rerun for `miro_api_token`. Either way, confirm the new token with +Headless, use a route the marketplace's plugin-reconfiguration convention gives in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record), and apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +For `miro_api_token`, take the preferred `claude plugin configure miro@` route, which +keeps the token off the command line: delete its values file afterwards, or pipe the value in from +your secrets manager. Do not rely on the install-rerun fallback for the token, since that +convention's record covers only a non-sensitive option. Either way, confirm the new token with `verify-api` in a **fresh session** (or after `/reload-plugins`, as step 3 says); a session that -has done neither still holds the old one, so never report an unobserved rotation. Do **not** -uninstall to rotate either: uninstalling drops the stored token and this -plugin's entire stored `pluginConfigs` entry, resetting every option in the README's Options -reference to its manifest default, and it can drop the `enabledPlugins` entry as well; a -`defaultEnabled: false` plugin reinstalls DISABLED, so a rotation ending at `install` delivers no tools. +has done neither can still hold the old one, so never report an unobserved rotation. Never +uninstall to rotate either; that section links what an uninstall deletes, and a +`defaultEnabled: false` plugin reinstalls DISABLED, so a rotation ending at `install` delivers no +tools. + +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. **Security note:** passing the token as a CLI argument records it in shell history (`.bash_history`, `.zsh_history`) and briefly exposes it in the process table diff --git a/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/case.yaml b/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/case.yaml index 0c91445fcab..a815ca05e7e 100644 --- a/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/case.yaml +++ b/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/case.yaml @@ -1,9 +1,9 @@ schema_version: "1.1" name: se-apply-user-layer-no-repo-write -description: 'Legacy setup 11. names-plugin-configure and names-values-stdin grade the save route, no-write, no-edit, no-git-add-or-commit and no-test-run the no-write rule; legacy-setup-11 (llm) quotes the legacy expectation. The narration stays in the prompt. Needs --scaffold --allow-tools Bash Write Edit.' +description: 'Legacy setup 11. names-plugin-configure grades the interactive save route and names-headless-configure the headless claude plugin configure route the plugin-reconfiguration contract gives, no-write, no-edit, no-git-add-or-commit and no-test-run the no-write rule; legacy-setup-11 (llm) quotes the legacy expectation. The narration stays in the prompt. Needs --scaffold --allow-tools Bash Write Edit.' tags: [setup, scaffold, no-network, legacy] runs: 3 -expected_outcome: 'Settles operators narrow and max_mutants 20 for userConfig and names /plugin configure mutation-testing@ (or claude plugin configure --values-stdin); writes nothing, runs no test, states the per-repository keys do not come from userConfig.' +expected_outcome: 'Settles operators narrow and max_mutants 20 for userConfig and names /plugin configure mutation-testing@ (or a headless route from the plugin-reconfiguration convention); writes nothing, runs no test, states the per-repository keys do not come from userConfig.' context: scaffold_script: scaffold.sh execution: @@ -17,11 +17,6 @@ graders: pattern: '/plugin configure mutation-testing@' flags: 'i' target: 'last_message' - - name: 'names-values-stdin' - type: 'regex' - pattern: '--values-stdin' - flags: 'i' - target: 'last_message' - name: 'no-write' type: 'tool_used' tool: 'Write' @@ -52,5 +47,9 @@ graders: arm: 'both' - name: 'legacy-setup-11' type: 'llm' - criteria: 'The run is /mutation-testing:setup apply user run on a scaffolded repository (A Python repo with no mutation-testing config). It meets the legacy expectation: "Treats the user layer as the plugin''s userConfig, not a ~/.claude/mutation-testing.md file. Settles only operators (narrow) and max_mutants (20) and names `/plugin configure mutation-testing@` as the way to save them, with the headless `claude plugin configure ... --values-stdin` form as the alternative. Writes no pluginConfigs value and no settings file itself, and writes no file in the repository: no docs/conventions/mutation-testing.yaml, no arid record, no .local.md personal override. Runs no test command. Says it skipped the write, tracking and commit steps, so `git status` in the repository is unchanged. States that the tool, diff target, mutate paths and test command are per repository and do not come from userConfig."' + criteria: 'The run is /mutation-testing:setup apply user run on a scaffolded repository (A Python repo with no mutation-testing config). It meets the legacy expectation: "Treats the user layer as the plugin''s userConfig, not a ~/.claude/mutation-testing.md file. Settles only operators (narrow) and max_mutants (20) and names `/plugin configure mutation-testing@` as the way to save them, with a headless route from the plugin-reconfiguration convention as the alternative. Writes no pluginConfigs value and no settings file itself, and writes no file in the repository: no docs/conventions/mutation-testing.yaml, no arid record, no .local.md personal override. Runs no test command. Says it skipped the write, tracking and commit steps, so `git status` in the repository is unchanged. States that the tool, diff target, mutate paths and test command are per repository and do not come from userConfig."' focus: 'trace' + - name: 'names-headless-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+mutation-testing@\S+' + target: 'last_message' diff --git a/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/samples/names-headless-configure.json b/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/samples/names-headless-configure.json new file mode 100644 index 00000000000..f18eb53a1ac --- /dev/null +++ b/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/samples/names-headless-configure.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Headless: claude plugin configure mutation-testing@melodic-software --values-stdin < values.json" + }, + { + "answer": "Or run `claude plugin configure mutation-testing@` with the values on stdin." + } + ], + "fail": [ + { + "answer": "Run /plugin configure mutation-testing@ and set operators and max_mutants.", + "why": "interactive route only; names no headless configure route" + }, + { + "answer": "claude plugin configure mutation-testing --values-stdin", + "why": "near miss: no marketplace" + } + ] +} diff --git a/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/samples/names-values-stdin.json b/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/samples/names-values-stdin.json deleted file mode 100644 index 682b840b49e..00000000000 --- a/plugins/mutation-testing/evals/se-apply-user-layer-no-repo-write/samples/names-values-stdin.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pass": [ - { - "answer": "or `claude plugin configure mutation-testing@m --values-stdin`", - "why": "headless" - } - ], - "fail": [ - { - "answer": "claude plugin configure", - "why": "no flag" - } - ] -} diff --git a/plugins/mutation-testing/reference/plugin-reconfiguration.md b/plugins/mutation-testing/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/mutation-testing/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/mutation-testing/skills/setup/SKILL.md b/plugins/mutation-testing/skills/setup/SKILL.md index f5b6749b135..993891ccd85 100644 --- a/plugins/mutation-testing/skills/setup/SKILL.md +++ b/plugins/mutation-testing/skills/setup/SKILL.md @@ -231,16 +231,31 @@ Steps 1 to 10 are the `team` flow. The other two layers change what is written: the effort cap, do not say the effort cap now applies: say the saved cap stays in force, since leaving the key out keeps it, and that clearing it is done in `/plugin configure mutation-testing@`. When this repository's team or local file already sets `operators` or `max-mutants`, - say that file overrides the user value here. Then name how to save them: - `/plugin configure mutation-testing@`, or headless - `claude plugin configure mutation-testing@ --values-stdin < values.json` with - `values.json` holding only the keys the user settled: `{"operators": ""}`, plus - `"max_mutants": ""` only when the user chose a cap (never write an empty or invented - cap), per the marketplace's plugin-reconfiguration convention - (, - which records what a key left out does; as of 2026-11-10, recheck when that heading is renamed - or the record changes what an omitted key does). - Never write `pluginConfigs` or any settings file yourself. Say that steps 7 to 10 were skipped + say that file overrides the user value here. Then name how to save them through Claude Code's + native flow: interactively with `/plugin configure mutation-testing@`, or by a + headless route as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record, and what a key left out does), with the scope and + failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Save only the keys the user settled: `operators`, plus `max_mutants` only when the user chose a + cap (never an empty or invented cap). Never uninstall to reconfigure; that section links what an + uninstall deletes. A `check` in a **fresh session** shows the saved value, since a same-session + `check` can still show the old one; report only an observed value, never an unobserved change. + Never write `pluginConfigs` or any settings file yourself. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. + + Say that steps 7 to 10 were skipped because nothing in the repository was written, so `git status` is unchanged, and that a repository's tool, diff target, mutate paths and test command still come from its team or local file or the audit's fallback. diff --git a/plugins/planning/reference/plugin-reconfiguration.md b/plugins/planning/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/planning/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/planning/skills/setup/SKILL.md b/plugins/planning/skills/setup/SKILL.md index 21ffc1deac4..821551d58f3 100644 --- a/plugins/planning/skills/setup/SKILL.md +++ b/plugins/planning/skills/setup/SKILL.md @@ -142,33 +142,27 @@ implement it by reference, do not restate it. Plugin-side notes only: `use_ask_user_question` is a native `userConfig` boolean (default `false`) governing whether the pipeline skills' question rounds render through `AskUserQuestion` or as inline prose. It is not a -consumer-project file this skill writes. Reconfigure through Claude Code's native flow, per the -marketplace's plugin-reconfiguration convention -(, -which owns the verified-version record): interactive `/plugin configure planning@` any -time. Headless, preferred: run -`claude plugin configure planning@ --values-stdin < values.json` from the project the -plugin is installed in, with `values.json` holding `{"use_ask_user_question": "true"}`: every -value, booleans included, written as a JSON string. Headless -fallback, for a Claude Code release without `plugin configure`: rerun -`claude plugin install planning@ -s --config use_ask_user_question=true` -(repeatable per key). Do **not** uninstall to reconfigure. For the rerun, pass the scope -`claude plugin list` reports, run from that project's directory for a `project`/`local` scope, and -from the home directory pass `user`. Check each route's own failure signal: read both its exit -code and its output before reporting a value saved. This skill never writes Claude Code user -settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**: a same-session -`check` can still report the old value, so report the observed effective value, never an -unobserved change. - -- **Pointer**: for the upstream specifics, fetch live - [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), - [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install), - [§ What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps), - [§ Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) - and [§ Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value). -- **As of**: 2026-11-10 -- **Recheck trigger**: one of those sections changes how a route takes or rejects a value, what an - uninstall deletes, what `-s` places, or when a saved value takes effect. +consumer-project file this skill writes. Reconfigure `use_ask_user_question` through Claude +Code's native flow, interactively with `/plugin configure planning@` or by a headless +route, as the marketplace's plugin-reconfiguration convention gives them in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record). Apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never +writes Claude Code user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh +session**, since a same-session `check` can still show the old value, and report the observed +effective value, never an unobserved change. + +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. ### Verify after remediation diff --git a/plugins/playwright/skills/setup/SKILL.md b/plugins/playwright/skills/setup/SKILL.md index 4f42412c74f..8a07adbf425 100644 --- a/plugins/playwright/skills/setup/SKILL.md +++ b/plugins/playwright/skills/setup/SKILL.md @@ -18,6 +18,12 @@ the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` diff --git a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml index 3dc4e9b5013..ee2b42139dd 100644 --- a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml +++ b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml @@ -3,27 +3,15 @@ name: su-apply-headless-disable-never-uninstalls graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure powershell-format@\S+\s+--values-stdin' + pattern: 'claude plugin configure powershell-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"powershell_format_enabled"\s*:\s*"false"' + pattern: '\bpowershell_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure powershell-format@\S+\s+--values-stdin[\s\S]*?claude plugin install powershell-format@\S+[^\n]*?--config[ =]powershell_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says to run the command from the project the plugin is installed in (claude plugin configure has no scope flag), and that the install rerun''s -s flag defaults to user, so the user passes the scope claude plugin list reports for this plugin, and passes user from the home directory.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'non-true-disables' type: 'llm' @@ -52,3 +40,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+powershell\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!powershell\-format@)(?:\w+|<[^>\s]+>))?)*\s+powershell\-format@\S+(?:\\\n|[^\n])*?--config[ =]powershell_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md index 508ed0afcab..0611851f4dd 100644 --- a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md +++ b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 157-183): claude plugin configure --values-stdin with every value a string is preferred; the claude plugin install --config rerun is the fallback for a release without plugin configure, and against an installed plugin it prints already installed and still writes the value; never uninstall (it resets every option); configure has no scope flag and runs from the project the plugin is installed in; -s defaults to user, so pass the scope claude plugin list reports, and user from the home directory; a same-session check still reports the old value, so rerun check in a fresh session. Step 6 (lines 81-83): any value other than true disables the hooks. The prompt raises each claim. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it, so that legacy expectation is dropped. Legacy 'ends by rerunning check' is now the fresh-session rerun of lines 180-183. no-plugin-cli-write guards a claude plugin write the run makes itself (line 180). Needs --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: reconfigure through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still show the old value, so rerun check in a fresh session. Step 6 (lines 81-83): any value other than true disables the hooks. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. Legacy 'ends by rerunning check' is now the fresh-session rerun. no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." tags: [powershell-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes the headless change to claude plugin configure powershell-format@ --values-stdin with {\"powershell_format_enabled\": \"false\"}, then the claude plugin install --config powershell_format_enabled=false rerun as the fallback that still writes on an installed plugin; rules out uninstalling; gives the scope rule; says any value other than true disables the hooks; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." +expected_outcome: "Routes the change to the native configure flow for powershell-format@ with powershell_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the fallback, the scope and the failure signals; rules out uninstalling; says any value other than true disables the hooks; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." --- /powershell-format:setup apply diff --git a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json index 517dde59b18..49104d0109f 100644 --- a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json +++ b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json @@ -2,24 +2,24 @@ "pass": [ { "answer": "`claude plugin configure powershell-format@ --values-stdin < values.json`" - }, - { - "answer": "claude plugin configure powershell-format@melodic-software --values-stdin", - "why": "real marketplace, two spaces" } ], "fail": [ { - "answer": "/plugin configure powershell-format@", - "why": "interactive route" + "answer": "claude plugin install powershell-format@m --config powershell_format_enabled=false", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure powershell-format --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { "answer": "", "why": "empty" + }, + { + "answer": "Run `/plugin configure powershell-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json deleted file mode 100644 index ef614f8a31b..00000000000 --- a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall powershell-format@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json new file mode 100644 index 00000000000..761713b04fc --- /dev/null +++ b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "Don't uninstall and reinstall: an uninstall is not a way to reconfigure. Use the configure route." + }, + { + "answer": "No. Never uninstall to change an option." + } + ], + "fail": [ + { + "answer": "Uninstall then reinstall with --config is the simplest route.", + "why": "advises uninstall" + }, + { + "answer": "You could uninstall and reinstall, but configure is quicker.", + "why": "near miss: leaves uninstall open as a route" + } + ] +} diff --git a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json index 3f0fe0b6002..f2058b6d1b2 100644 --- a/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json +++ b/plugins/powershell-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json @@ -4,23 +4,18 @@ "answer": "`{\"powershell_format_enabled\": \"false\"}`" }, { - "answer": "{\"powershell_format_enabled\":\"false\"}", - "why": "no space" + "answer": "Set powershell_format_enabled to false." } ], "fail": [ - { - "answer": "{\"powershell_format_enabled\": false}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "powershell_format_enabled=false", - "why": "the --config form, not the JSON" - }, { "answer": "{\"powershell_format_enabled\": \"true\"}", "why": "wrong value" }, + { + "answer": "{\"powershell_format\": \"false\"}", + "why": "near miss: wrong key" + }, { "answer": "", "why": "empty" diff --git a/plugins/powershell-format/reference/plugin-reconfiguration.md b/plugins/powershell-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/powershell-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/powershell-format/skills/setup/SKILL.md b/plugins/powershell-format/skills/setup/SKILL.md index 5d10fbd8136..e303606928e 100644 --- a/plugins/powershell-format/skills/setup/SKILL.md +++ b/plugins/powershell-format/skills/setup/SKILL.md @@ -159,33 +159,27 @@ Run `check`, then for each finding point at the resolution. This skill installs settings file at or below the project root opts the repo in, but this skill does not write it. The settings file is the executed-adjacent trust boundary above; the choice and the edit belong to the consumer. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure powershell-format@` - any time. Headless, preferred: run - `claude plugin configure powershell-format@ --values-stdin < values.json` from the - project the plugin is installed in, with `values.json` holding - `{"powershell_format_enabled": "true"}` (every value a string, booleans included; keys left out - keep their saved values). PowerShell reserves `<`, so there pipe the file instead: - `Get-Content values.json | claude plugin configure powershell-format@ --values-stdin`. - Headless fallback, for a Claude Code release without - `plugin configure`: rerun - `claude plugin install powershell-format@ -s --config powershell_format_enabled=true` - (repeatable per key). Against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops the plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. Either route stores the value in user settings. `claude plugin configure` has - no scope flag and finds only an install visible from the working directory. For the rerun, `-s` - defaults to `user`; pass the scope `claude plugin list` reports, and run from that project's - directory for a `project`/`local` scope, or the rerun adds a second install record at the scope - passed and enables the plugin there; from the home directory pass `user`. Check each route's own - failure signal: `claude plugin configure` rejects a bad value with exit 1 and saves nothing, but - stores an empty string as `""`; the rerun prints a warning for a rejected value yet exits 0, so - read its output. This skill never writes user settings or `pluginConfigs`. Afterwards rerun - `check` in a **fresh session**. The rendered `${user_config.*}` and the hook's - `CLAUDE_PLUGIN_OPTION_*` are fixed at session start, so a same-session `check` still reports - the OLD value; report the observed effective value, never an unobserved change. +- toggle off: set `powershell_format_enabled` to `true` through Claude Code's native flow, + interactively with `/plugin configure powershell-format@` or by a headless route, + as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. After pointing at a remediation, re-run the relevant `check` probe live via Bash and report its actual result. Never claim resolved on the reader's report that they installed something. diff --git a/plugins/rate-limit-guard/reference/plugin-reconfiguration.md b/plugins/rate-limit-guard/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/rate-limit-guard/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/rate-limit-guard/skills/setup/SKILL.md b/plugins/rate-limit-guard/skills/setup/SKILL.md index 0dfa1bdb242..ccaeeceb3c8 100644 --- a/plugins/rate-limit-guard/skills/setup/SKILL.md +++ b/plugins/rate-limit-guard/skills/setup/SKILL.md @@ -18,6 +18,12 @@ tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run the `command -v node` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Check-only setup under the Check-only carve-out (`docs/plugin-philosophy.md` "Setup is explicit @@ -27,33 +33,25 @@ and no `apply` is offered because there is nothing it could conformingly write. qualify it: - **Native `userConfig`**: eight options whose only stored home is the `pluginConfigs` setup must - never write. Reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure - rate-limit-guard@` any time. Headless, preferred: run `claude plugin configure - rate-limit-guard@ --values-stdin < values.json` from the project the plugin is - installed in, with `values.json` holding `{"": ""}` and every value, booleans - included, written as a JSON string. Headless fallback, for a Claude Code release without - `plugin configure`: rerun `claude plugin install rate-limit-guard@ -s - --config =` (repeatable per key) against the installed plugin. Do **not** uninstall - to reconfigure. For the rerun, pass the scope `claude plugin list` reports and run from that - project's directory for a `project`/`local` scope; from the home directory pass `user`. Check - each route's own failure signal: read both its exit code and its output before reporting a - value saved. Afterwards rerun `check` in a **fresh session**: a same-session `check` still - renders the old value (see the `check` note on rendered values below), so report the observed - value, never an unobserved change. The convention's - [caveats section](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/plugin-reconfiguration/README.md#caveats-every-setup-skills-short-form-carries) - records why for each of these rules; this skill does not restate it. - - **Pointer**: when what a route prints, rejects, stores or resets is in doubt, fetch - [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), - [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) and - [§ What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps) + never write. Reconfigure through Claude Code's native flow, interactively with + `/plugin configure rate-limit-guard@` or by a headless route, as the marketplace's + plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. Afterwards rerun + `check` in a **fresh session**, since a same-session `check` can still show the old value, and + report the observed effective value, never an unobserved change. + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) live. - - **As of**: 2026-11-10 - - **Recheck trigger**: either convention heading is renamed, or one of those sections changes - how a route reports a rejected value, what a rerun at another scope adds, or what an - uninstall deletes. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - **External prerequisites**: `node` and the Claude Code version, which `check` probes and whose install is the person's. @@ -170,10 +168,15 @@ line. it. Report the value as invalid and the default as what runs. A value of the wrong type (text for a number or a switch) stops the module loading: report it with step 2's row. - Every option except `rate_limit_guard_enabled` acts only where step 2 found the mod running. - - The rendered values are injected when this skill loads, and the module reads its options when - it loads; Claude Code reloads a module when its options change, so a changed value acts from - the next event. The StopFailure hook reads its value at session start. Report the observed - value, never an unobserved change. + - A change made now is observed only in a **fresh session**: rerun this check there and report + the observed value, never an unobserved change. + + - **Pointer**: for when a saved value reaches skill content and hook processes, fetch + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes where or when a saved value is substituted or + exported. ## Uninstalling diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/case.yaml b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/case.yaml index b6de21b55b1..d30d461f1bd 100644 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/case.yaml +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/case.yaml @@ -3,28 +3,19 @@ name: setup-headless-toggle-configure graders: - name: configure-route type: regex - pattern: 'claude plugin configure repo-hygiene@\S+\s+--values-stdin' + pattern: 'claude plugin configure repo-hygiene@\S+' target: last_message - - name: string-true-value + - name: value-true type: regex - pattern: '"clean_destructive_guard_enabled"\s*:\s*"true"' + pattern: '\bclean_destructive_guard_enabled\b[^\n]{0,40}?\btrue\b' target: last_message - - name: fallback-after-configure - type: regex - pattern: 'claude plugin configure repo-hygiene@\S+\s+--values-stdin[\s\S]*?claude plugin install repo-hygiene@\S+[^\n]*?--config[ =]clean_destructive_guard_enabled=true\b' - target: last_message - - name: already-installed-still-writes - type: regex - pattern: '\balready installed\b[^\n]{0,160}\b(?:still|also)\b[^\n]{0,40}\bwrites?\b|\bstill writes?\b[^\n]{0,160}\balready installed\b' - flags: 'i' - target: last_message - - name: no-uninstall-with-reason + - name: no-uninstall type: llm - criteria: 'The reply does not tell the user to uninstall the plugin (for example claude plugin uninstall, then reinstall) in order to reconfigure it, and says why: uninstalling drops the plugin''s whole stored pluginConfigs entry and resets every option to its manifest default.' + criteria: 'The reply does not tell the user to uninstall the plugin (for example claude plugin uninstall, then reinstall) in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: last_message - name: cites-reconfiguration-convention type: regex - pattern: '\bplugin[- ]reconfiguration\b' + pattern: '\bplugin[- ]reconfiguration\b|\breconfiguration[- ](?:convention|contract|routes|guide)\b' flags: 'i' target: last_message - name: no-restated-versions @@ -32,18 +23,27 @@ graders: pattern: '\b2\.1\.(?!285\b)\d{3}\b' match: not_contains target: last_message - - name: scope-rule - type: llm - criteria: 'The reply says both routes run from the project the plugin is installed in (claude plugin configure has no scope flag); that for the install rerun the -s flag defaults to user, so the user passes the scope claude plugin list reports for this plugin, or the rerun adds a second install record at the scope passed; and that the value itself lands in user settings.' - focus: last_message - name: fresh-session-observed-value type: llm - criteria: 'The reply does not claim the guard is now on. It tells the user to rerun check in a fresh session, because the toggle value is rendered when the skill loads and a same-session check still reports the old value, and to report the effective value that check observes.' + criteria: 'The reply does not claim the guard is now on. It tells the user to confirm the change by rerunning the setup check in a fresh session, since a same-session check can still report the old value.' focus: last_message - name: no-plugin-command-run type: tool_used tool: 'Bash' - input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b' + input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b(?!\s+--help\b)' min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+repo\-hygiene@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!repo\-hygiene@)(?:\w+|<[^>\s]+>))?)*\s+repo\-hygiene@\S+(?:\\\n|[^\n])*?--config[ =]clean_destructive_guard_enabled=true\b' + target: 'last_message' + - name: 'already-installed-still-writes' + type: 'regex' + pattern: '\balready[- ]installed\b(?:(?!\bnot\b|n[''’]t\b|\bnothing\b)[^\n]){0,160}?\b(?:writes?|saves?)\b|\bstill (?:writes?|saves?)\b[^\n]{0,160}\balready[- ]installed\b|\b(?:writes?|saves?)\b[^\n]{0,40}\b(?:on|against|to)\s+an?\s+already[- ]installed\b' + flags: 'i' + target: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/matchers.json b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/matchers.json index 97dff718962..d48663b799c 100644 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/matchers.json +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/matchers.json @@ -3,6 +3,6 @@ { "use": "command_position_multishell" }, - "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b" + "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b(?!\\s+--help\\b)" ] } diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/prompt.md b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/prompt.md index a857f787481..1b4dc3981f0 100644 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/prompt.md +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/prompt.md @@ -1,10 +1,10 @@ --- -description: 'Headless toggle change routes to claude plugin configure, never an uninstall. setup is user-invoked only, so the case starts with its slash command and carries no Skill grader. setup SKILL.md lines 113-135: per the plugin-reconfiguration convention (which owns the verified-version record), headless preferred is claude plugin configure repo-hygiene@ --values-stdin with {"clean_destructive_guard_enabled": "true"} (every value a string; 2.1.285 or later); fallback is the claude plugin install ... -s --config clean_destructive_guard_enabled=true rerun, which on an installed plugin prints already installed and still writes the value; never uninstall, which drops the stored pluginConfigs entry; run either route from the project the plugin is installed in, pass the scope claude plugin list reports, and the value lands in user settings; rerun check in a fresh session and report the observed value. Lines 132 and 142-143: setup never writes user settings or pluginConfigs, so no-plugin-command-run fails a claude plugin write the run makes itself. no-restated-versions fails any Claude Code version other than 2.1.285 (the convention owns the measured releases). Bash is in allowed_tools for the SKILL.md pre-computed ghq line and so no-plugin-command-run can fail. Needs --allow-tools Bash.' +description: 'Headless toggle change routes to the native configure flow, never an uninstall. setup is user-invoked only, so the case starts with its slash command and carries no Skill grader. setup SKILL.md "Toggle off (or on)": set clean_destructive_guard_enabled through the native configure flow, with the headless routes and their scope and failure-signal caveats cited from the plugin-reconfiguration convention rather than restated (cites-reconfiguration-convention); never uninstall to reconfigure; rerun check in a fresh session and report the observed value. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, already-installed-still-writes and scope-rule grade them there. setup never writes user settings or pluginConfigs, so no-plugin-command-run fails a claude plugin write the run makes itself. no-restated-versions fails any Claude Code version other than 2.1.285 (the convention owns the measured releases). Bash is in allowed_tools for the SKILL.md pre-computed ghq line and so no-plugin-command-run can fail. Needs --allow-tools Bash.' tags: [repo-hygiene, setup, outcome, reconfigure, must-not, no-network] max_turns: 15 timeout_seconds: 600 allowed_tools: [Skill, Read, Glob, Grep, Bash] -expected_outcome: 'Routes the change to claude plugin configure repo-hygiene@ --values-stdin with {"clean_destructive_guard_enabled": "true"}, then the claude plugin install --config rerun as the fallback that still writes on an installed plugin; rules out uninstalling and says why; cites the plugin-reconfiguration convention; gives the scope rule; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' +expected_outcome: 'Routes the change to the native configure flow for repo-hygiene@ with clean_destructive_guard_enabled set to true, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' --- /repo-hygiene:setup Turn clean_destructive_guard_enabled back on headlessly. I cannot use the interactive configure prompt from this session. diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/already-installed-still-writes.json b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/already-installed-still-writes.json index d35ae07ca14..72d8ab38e63 100644 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/already-installed-still-writes.json +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/already-installed-still-writes.json @@ -5,6 +5,18 @@ }, { "answer": "It still writes the value even though it prints `already installed`." + }, + { + "answer": "The rerun is the fallback because it saves the value on an already-installed plugin." + }, + { + "answer": "Already installed: the rerun still saves the value." + }, + { + "answer": "On an already-installed plugin this prints `already installed` and only saves the value." + }, + { + "answer": "On a plugin that's already installed, a rerun at the same scope prints `already installed` and saves only the option." } ], "fail": [ @@ -14,6 +26,18 @@ }, { "answer": "" + }, + { + "answer": "Fallback: claude plugin install @ -s --config KEY=VALUE.", + "why": "gives the fallback but omits that it saves on an already-installed plugin" + }, + { + "answer": "If it is already installed, the rerun does not save the value.", + "why": "says nothing is saved" + }, + { + "answer": "Already installed: the rerun won't write anything.", + "why": "says nothing is written" } ] } diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/configure-route.json b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/configure-route.json index 9388c0d1714..4d609185e20 100644 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/configure-route.json +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/configure-route.json @@ -6,10 +6,12 @@ ], "fail": [ { - "answer": "claude plugin configure repo-hygiene@melodic" + "answer": "claude plugin install repo-hygiene@m --config clean_destructive_guard_enabled=true", + "why": "an install rerun, not the configure flow" }, { - "answer": "/plugin configure" + "answer": "/plugin configure", + "why": "near miss: no plugin id" } ] } diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/no-plugin-command-run.json b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/no-plugin-command-run.json index d46b9eb1e4d..82c4ef3129f 100644 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/no-plugin-command-run.json +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/no-plugin-command-run.json @@ -70,6 +70,18 @@ } ], "why": "inside echo" + }, + { + "answer": [ + { + "tool": "Bash", + "input": { + "command": "claude plugin configure --help 2>&1 | head -40", + "description": "Show configure flags" + } + } + ], + "why": "a read-only --help probe" } ], "fail": [ @@ -83,7 +95,7 @@ } } ], - "why": "uninstall resets every option (setup:142-144)" + "why": "uninstall to reconfigure" }, { "answer": [ diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/string-true-value.json b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/string-true-value.json deleted file mode 100644 index 850fff57ec7..00000000000 --- a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/string-true-value.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "pass": [ - { - "answer": "{\"clean_destructive_guard_enabled\": \"true\"}" - } - ], - "fail": [ - { - "answer": "{\"clean_destructive_guard_enabled\": true}", - "why": "boolean, not a string" - } - ] -} diff --git a/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/value-true.json b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/value-true.json new file mode 100644 index 00000000000..9d7710b9f89 --- /dev/null +++ b/plugins/repo-hygiene/evals/setup-headless-toggle-configure/samples/value-true.json @@ -0,0 +1,20 @@ +{ + "pass": [ + { + "answer": "{\"clean_destructive_guard_enabled\": \"true\"}" + }, + { + "answer": "Set clean_destructive_guard_enabled to true." + } + ], + "fail": [ + { + "answer": "{\"clean_destructive_guard_enabled\": \"false\"}", + "why": "wrong value" + }, + { + "answer": "{\"clean_destructive_guard\": \"true\"}", + "why": "wrong key" + } + ] +} diff --git a/plugins/repo-hygiene/reference/plugin-reconfiguration.md b/plugins/repo-hygiene/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/repo-hygiene/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/repo-hygiene/skills/setup/SKILL.md b/plugins/repo-hygiene/skills/setup/SKILL.md index fe031ff6fa0..defe17535b6 100644 --- a/plugins/repo-hygiene/skills/setup/SKILL.md +++ b/plugins/repo-hygiene/skills/setup/SKILL.md @@ -18,6 +18,12 @@ the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + `git` is probed in the body, not here ([gather-block convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/conventions/gather-block/README.md#the-pre-compute-block-runs-as-one-shell-invocation), "The pre-compute block runs as one shell invocation"). @@ -91,8 +97,13 @@ Install nothing, and run no mutating tier. - Report the effective `${user_config.clean_destructive_guard_enabled}` (an unexpanded token or an empty value means the manifest default `true`). Read it from this text as rendered; never echo it through Bash, where an unexpanded token is invalid shell syntax and the call is - refused. The rendered value is injected when this - skill loads, so a change made now is observed only in a **fresh session**. + refused. A change made now is observed only in a **fresh session**. + + - **Pointer**: for when a saved value reaches skill content, fetch + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes where or when a saved value is substituted. - The option is **user-scoped**: plugin option values are read from user, `--settings`, and managed settings only, never from a project's `.claude/settings.json`. So there is no per-repository value of this toggle. To vary the behavior for one repository, enable or disable @@ -112,29 +123,27 @@ after everything passes changes nothing and reports "already configured": - **Missing `node`:** install Node.js () and put it on `PATH`. Until then the destructive guard is not enforced. - **Missing Git Bash on Windows:** install Git for Windows; nothing in this plugin runs without it. -- **Toggle off (or on):** reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure repo-hygiene@` - any time. Headless, preferred (Claude Code 2.1.285 or later): - `claude plugin configure repo-hygiene@ --values-stdin < values.json`, with - `values.json` holding `{"clean_destructive_guard_enabled": "true"}` (every value a string, - booleans included; keys left out keep their saved values). Headless fallback (an older - release): `claude plugin install repo-hygiene@ -s --config clean_destructive_guard_enabled=true` - (repeatable per key); against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops this plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference (`${CLAUDE_PLUGIN_ROOT}/README.md`, read with Read if needed) to its - manifest default. Scope: the value always lands in user settings. Run either route from the - project the plugin is installed in (`configure` has no scope flag). For the rerun, `-s` - defaults to `user`, so pass the scope `claude plugin list` reports, or the rerun adds a second - install record at the scope passed and enables the plugin there; from the home directory, where - the list can label one settings file both `user` and `project`, pass `user`. Check each route's own failure - signal: `configure` rejects a bad value with exit 1 and saves nothing, but stores an empty - string as `""`; the rerun prints a warning for a rejected value yet exits 0, so read its output. - This skill never writes user settings or `pluginConfigs`. Afterwards rerun - `check` in a **fresh session**. The rendered token is injected at skill load, so a same-session - `check` still reports the OLD value; report the observed effective toggle value, never an - unobserved change. +- **Toggle off (or on):** set `clean_destructive_guard_enabled` to `true` or `false` through + Claude Code's native flow, interactively with `/plugin configure repo-hygiene@` or + by a headless route, as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective toggle + value, never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. ## What this skill does NOT do diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml index 2a4f0f664e8..0c26a04b0cb 100644 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/case.yaml @@ -3,27 +3,15 @@ name: su-apply-headless-disable-never-uninstalls graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure ruff-format@\S+\s+--values-stdin' + pattern: 'claude plugin configure ruff-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"ruff_format_enabled"\s*:\s*"false"' + pattern: '\bruff_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure ruff-format@\S+\s+--values-stdin[\s\S]*?claude plugin install ruff-format@\S+[^\n]*?--config[ =]ruff_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says to run the command from the project the plugin is installed in (claude plugin configure has no scope flag), and that the install rerun''s -s flag defaults to user, so the user passes the scope claude plugin list reports for this plugin, and passes user from the home directory.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -32,7 +20,7 @@ graders: - name: 'no-plugin-cli-write' type: 'tool_used' tool: 'Bash' - input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b' + input_match: '"command":"(?:(?:[^"\\]|\\.)*?(?:&&|\|\||[;|&(`]|\{(?=\s)|\$\(|\\n|\s-exec(?:dir)?(?=\s)))?(?:\s|\\\\\\n)*(?:(?:\w+=\S*|(?:sudo|doas|env|exec|eval|time|nice|nohup|xargs|then|do|else|!)(?:\s+-[^\s\"\\]+)*|command(?:\s+-p)?|(?:python[\d.]*|py)(?:\.exe)?(?:\s+-\w+)*?\s+-m|timeout(?:\s+-\S+)*\s+\S+)(?:\s|\\\\\\n)+|(?:(?:bash|dash|zsh|sh)(?:\s+-[^\s\"\\]+)*?\s+-\w*c\w*|(?:powershell|pwsh)(?:\.exe)?(?:\s+-\w+)*?\s+-(?:Command|c)|cmd(?:\.exe)?\s+/[cC])\s+(?:\\\"|'')?(?:\s|\\\\\\n)*)*(?:\\\"|'')?(?:[^\s\"\\;&|='']*/)?claude(?:\.exe)?(?:\\\"|'')?(?:(?:\s|\\\\\\n)+-[^\s\"\\]+)*(?:\s|\\\\\\n)+plugins?(?:\s|\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\b(?!\s+--help\b)' min: 0 max: 0 arm: 'both' @@ -48,3 +36,15 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+ruff\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!ruff\-format@)(?:\w+|<[^>\s]+>))?)*\s+ruff\-format@\S+(?:\\\n|[^\n])*?--config[ =]ruff_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/matchers.json b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/matchers.json index db8928b2b6b..13de48b184f 100644 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/matchers.json +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/matchers.json @@ -3,6 +3,6 @@ { "use": "command_position_multishell" }, - "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b" + "(?:\\\\\\\"|')?(?:[^\\s\\\"\\\\;&|=']*/)?claude(?:\\.exe)?(?:\\\\\\\"|')?(?:(?:\\s|\\\\\\\\\\\\n)+-[^\\s\\\"\\\\]+)*(?:\\s|\\\\\\\\\\\\n)+plugins?(?:\\s|\\\\\\\\\\\\n)+(?:install|uninstall|remove|configure|enable|disable|update)\\b(?!\\s+--help\\b)" ] } diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md index 77ac66a1d97..1fcb9ea2891 100644 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 177-199): claude plugin configure --values-stdin with every value a string is preferred (2.1.285 or later); the claude plugin install --config rerun is the fallback, and against an installed plugin it prints already installed and still writes the value; never uninstall (it resets every option); configure has no scope flag and either route runs from the project the plugin is installed in; -s defaults to user, so pass the scope claude plugin list reports, and user from the home directory; a same-session check still reports the old value, so rerun check in a fresh session. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: set ruff_format_enabled to false through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still report the old value, so rerun check in a fresh session. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and fallback-after-configure, rerun-still-writes and scope-rule grade them there. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. Needs --allow-tools Bash Write Edit so the must-not graders can fail." tags: [ruff-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: 'Routes the headless change to claude plugin configure ruff-format@ --values-stdin with {"ruff_format_enabled": "false"}, then the claude plugin install --config ruff_format_enabled=false rerun as the fallback that still writes on an installed plugin; rules out uninstalling; gives the scope rule; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' +expected_outcome: 'Routes the headless change to the native configure flow for ruff-format@ with ruff_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself.' --- /ruff-format:setup apply diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json index ef563384f04..661014f3896 100644 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/configure-route.json @@ -2,24 +2,24 @@ "pass": [ { "answer": "`claude plugin configure ruff-format@ --values-stdin < values.json`" - }, - { - "answer": "claude plugin configure ruff-format@melodic-software --values-stdin", - "why": "real marketplace, two spaces" } ], "fail": [ { - "answer": "/plugin configure ruff-format@", - "why": "interactive route" + "answer": "claude plugin install ruff-format@m --config ruff_format_enabled=false", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure ruff-format --values-stdin", - "why": "no marketplace" + "why": "near miss: no marketplace" }, { "answer": "", "why": "empty" + }, + { + "answer": "Run `/plugin configure ruff-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-plugin-cli-write.json b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-plugin-cli-write.json index c0028a1f46c..53f8d33b57f 100644 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-plugin-cli-write.json +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-plugin-cli-write.json @@ -83,7 +83,7 @@ } } ], - "why": "uninstall resets every option (setup:187-189)" + "why": "uninstall to reconfigure" }, { "answer": [ diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json deleted file mode 100644 index d32f2f06dc7..00000000000 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Don't uninstall to reconfigure: uninstalling drops the plugin's whole stored pluginConfigs entry, resetting every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Simplest route: `claude plugin uninstall ruff-format@`, then reinstall with the option set.", - "why": "advises uninstall" - }, - { - "answer": "Don't uninstall it.", - "why": "near miss: no reason" - } - ] -} diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json new file mode 100644 index 00000000000..9e32c867c17 --- /dev/null +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/no-uninstall.json @@ -0,0 +1,13 @@ +{ + "pass": [ + { + "answer": "Don't uninstall to reconfigure; change the value through the configure flow instead." + } + ], + "fail": [ + { + "answer": "Simplest route: `claude plugin uninstall ruff-format@`, then reinstall with the option set.", + "why": "advises uninstall" + } + ] +} diff --git a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json index 4ab18d18ebe..005cffa4468 100644 --- a/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json +++ b/plugins/ruff-format/evals/su-apply-headless-disable-never-uninstalls/samples/values-false.json @@ -4,23 +4,18 @@ "answer": "`{\"ruff_format_enabled\": \"false\"}`" }, { - "answer": "{\"ruff_format_enabled\":\"false\"}", - "why": "no space" + "answer": "Set ruff_format_enabled to false." } ], "fail": [ - { - "answer": "{\"ruff_format_enabled\": false}", - "why": "unquoted boolean; every value is a string" - }, - { - "answer": "ruff_format_enabled=false", - "why": "the --config form, not the JSON" - }, { "answer": "{\"ruff_format_enabled\": \"true\"}", "why": "wrong value" }, + { + "answer": "{\"ruff_format\": \"false\"}", + "why": "wrong key" + }, { "answer": "", "why": "empty" diff --git a/plugins/ruff-format/reference/plugin-reconfiguration.md b/plugins/ruff-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/ruff-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/ruff-format/skills/setup/SKILL.md b/plugins/ruff-format/skills/setup/SKILL.md index 4b2b932f741..3f2d59e6b09 100644 --- a/plugins/ruff-format/skills/setup/SKILL.md +++ b/plugins/ruff-format/skills/setup/SKILL.md @@ -22,6 +22,12 @@ the tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run that tool's `command -v` probe via Bash instead. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. + ## Purpose Thin check-centric setup per the uniform setup contract (`docs/plugin-philosophy.md` @@ -182,29 +188,27 @@ only points: - missing `jq` / Bash: platform install instructions from the README Requirements section; this skill never installs system packages. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure ruff-format@` - any time. Headless, preferred (Claude Code 2.1.285 or later): - `claude plugin configure ruff-format@ --values-stdin < values.json`, with - `values.json` holding `{"ruff_format_enabled": "false"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback (an older release): - `claude plugin install ruff-format@ -s --config ruff_format_enabled=false` - (repeatable per key); against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops this plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. Scope: the value always lands in user settings. Run either route from the - project the plugin is installed in (`configure` has no scope flag). For the rerun, `-s` - defaults to `user`, so pass the scope `claude plugin list` reports, or the rerun adds a second - install record at the scope passed and enables the plugin there; from the home directory, where - the list can label one settings file both `user` and `project`, pass `user`. Check each route's own failure - signal: `configure` rejects a bad value with exit 1 and saves nothing, but stores an empty - string as `""`; the rerun prints a warning for a rejected value yet exits 0, so read its output. - This skill never writes user settings or `pluginConfigs`. Afterwards rerun - `check` in a **fresh session**. The rendered `${user_config.*}` is injected at skill load and - each hook's `CLAUDE_PLUGIN_OPTION_*` is fixed at session start, so a same-session `check` still - reports the OLD value; report the observed effective value, never an unobserved change. +- toggle off: set `ruff_format_enabled` to `false` through Claude Code's native flow, + interactively with `/plugin configure ruff-format@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - no Ruff config: offer to create a minimal Ruff config in the repository root only when explicitly asked. The plugin imposes no rules of its own. diff --git a/plugins/session-flow/reference/plugin-reconfiguration.md b/plugins/session-flow/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/session-flow/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/session-flow/skills/setup/SKILL.md b/plugins/session-flow/skills/setup/SKILL.md index 64590481a78..c856e1644cc 100644 --- a/plugins/session-flow/skills/setup/SKILL.md +++ b/plugins/session-flow/skills/setup/SKILL.md @@ -142,29 +142,26 @@ For the observer there is no write path. For each FAIL, `check` closes by offeri tool, or route observer reconfiguration through Claude Code's native flow. Do not write the plugin cache, Claude Code user settings, or `pluginConfigs`. -Reconfigure the observer's `userConfig` keys through Claude Code's native flow, per the -marketplace's plugin-reconfiguration convention -(, -which owns the verified-version record): interactive `/plugin configure session-flow@` -any time. Headless, preferred (Claude Code 2.1.285 or later): -`claude plugin configure session-flow@ --values-stdin < values.json`, with -`values.json` holding `{"": ""}` (every value a string, booleans included; keys left -out keep their saved values). Headless fallback (an older release): -`claude plugin install session-flow@ -s --config =` (repeatable -per key); against an already-installed plugin it prints `already installed` and still writes the -value. Do **not** uninstall to reconfigure: that drops the stored `pluginConfigs` entry outright, -resetting every option in the README's Options reference to its manifest default, with nothing -left to read the old values from. Scope: the value always lands in user settings. Run either route -from the project the plugin is installed in (`configure` has no scope flag). For the rerun, `-s` -defaults to `user`, so pass the scope `claude plugin list` reports, or the rerun adds a second -install record at the scope passed and enables the plugin there; from the home directory, where the -list can label one settings file both `user` and `project`, pass `user`. Check each route's own failure signal: -`configure` rejects a bad value with exit 1 and saves nothing, but stores an empty string as `""`; -the rerun prints a warning for a rejected value yet exits 0, so read its output. Afterwards rerun -`check` in a **fresh session**: -the rendered `${user_config.*}` is injected at skill load and each hook's `CLAUDE_PLUGIN_OPTION_*` -is fixed at session start, so a same-session `check` still reports the OLD value; report the -observed effective value, never an unobserved change. +Reconfigure the observer's `userConfig` keys through Claude Code's native flow, interactively with +`/plugin configure session-flow@` or by a headless route, as the marketplace's +plugin-reconfiguration convention gives them in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record). Apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +Never uninstall to reconfigure; that section links what an uninstall deletes. Afterwards rerun +`check` in a **fresh session**, since a same-session `check` can still show the old value, and +report the observed effective value, never an unobserved change. + +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. ## Output diff --git a/plugins/skill-quality/evals/su-check-missing-root-routes-to-configure/prompt.md b/plugins/skill-quality/evals/su-check-missing-root-routes-to-configure/prompt.md index d3c4be18e8a..11c93a7c193 100644 --- a/plugins/skill-quality/evals/su-check-missing-root-routes-to-configure/prompt.md +++ b/plugins/skill-quality/evals/su-check-missing-root-routes-to-configure/prompt.md @@ -1,5 +1,5 @@ --- -description: "/skill-quality:setup check with skills_root unset and no SKILL.md anywhere must FAIL rather than claim success, and route the fix to Claude Code's own plugin configure flow, never a hand edit of settings. setup SKILL.md: FAIL when the root is absent or empty, never claim success for a missing directory; reconfigure through /plugin configure or claude plugin configure --values-stdin; never write user settings or pluginConfigs. Also carries legacy setup eval 3: no-write, no-edit and no-mkdir grade that setup neither creates .claude/skills nor writes settings for the conventional default. Needs --scaffold." +description: "/skill-quality:setup check with skills_root unset and no SKILL.md anywhere must FAIL rather than claim success, and route the fix to Claude Code's own plugin configure flow, never a hand edit of settings. setup SKILL.md: FAIL when the root is absent or empty, never claim success for a missing directory; reconfigure through the native configure flow (/plugin configure, or a headless claude plugin configure route); never write user settings or pluginConfigs. Also carries legacy setup eval 3: no-write, no-edit and no-mkdir grade that setup neither creates .claude/skills nor writes settings for the conventional default. Needs --scaffold." tags: [skill-quality, setup, check, outcome, read-only, scaffold] max_turns: 10 timeout_seconds: 240 diff --git a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/case.yaml b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/case.yaml index cdb78a5a7a0..ef223d9b314 100644 --- a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/case.yaml +++ b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/case.yaml @@ -14,21 +14,13 @@ graders: min: 0 max: 0 arm: both - - name: configure-values-stdin + - name: configure-route type: regex - pattern: 'claude plugin configure skill-quality@\S*\s+--values-stdin\b' + pattern: '(?:/plugin|claude plugin) configure skill-quality@\S+' target: last_message - name: values-skills-root type: regex - pattern: '"skills_root"\s*:\s*"' - target: last_message - - name: install-config-fallback - type: regex - pattern: 'claude plugin install skill-quality@[^\n]*?--config\s+skills_root=' - target: last_message - - name: scope-from-plugin-list - type: regex - pattern: '\bclaude plugin list\b' + pattern: '\bskills_root\b[^\n]{0,40}?\bpackages/agent/skills\b' target: last_message - name: names-reconfiguration-convention type: regex @@ -40,15 +32,10 @@ graders: pattern: '\b(?:fresh|new|next)\s+(?:Claude(?: Code)?\s+)?session\b|\brestart\w*\s+(?:Claude(?: Code)?|the session|your session)\b' flags: 'i' target: last_message - - name: uninstall-reason - type: regex - pattern: '\bmanifest defaults?\b|\breset\w*\s+(?:every|all)\s+(?:\w+\s+)?options?\b|\bdrops?\s+(?:the\s+)?(?:whole|entire|stored)\b[^\n]{0,40}\b(?:config\w*|options?|pluginConfigs)\b' - flags: 'i' - target: last_message - name: routes-through-claude-code type: llm focus: last_message - criteria: "PASS only if the reply verifies packages/agent/skills as the skills root with its skill count, without asking the user to confirm first; routes the persistent skills_root change through Claude Code's plugin configuration, giving `claude plugin configure skill-quality@ --values-stdin` with a values file setting skills_root as the preferred headless route, run from the project the plugin is installed in, and a `claude plugin install skill-quality@ --config skills_root=` rerun against the already-installed plugin, passing the scope `claude plugin list` reports, as the fallback (interactive /plugin configure may also be named); points at the marketplace's plugin-reconfiguration convention for the verified-version record; never prescribes `claude plugin uninstall` to reconfigure and says why (it resets every option to its manifest default, dropping the stored configuration); and says to verify by rerunning check in a fresh session. Explaining the one-run CHECK_SKILL_SKILLS_ROOT override is optional. FAIL if it edits or proposes editing settings.json, settings.local.json or pluginConfigs directly, says it changed the setting itself, exports or persists CHECK_SKILL_SKILLS_ROOT (a shell profile, settings env or .envrc), prescribes uninstall then reinstall, or leaves out any route requirement above." + criteria: "PASS only if the reply verifies packages/agent/skills as the skills root with its skill count, without asking the user to confirm first; routes the persistent skills_root change through Claude Code's native configure flow for skill-quality@ (interactive /plugin configure or a headless route), setting skills_root to packages/agent/skills; gives the headless claude plugin configure route or points at the plugin-reconfiguration convention for it; never prescribes `claude plugin uninstall` to reconfigure; and says to verify by rerunning check in a fresh session. Explaining the one-run CHECK_SKILL_SKILLS_ROOT override is optional. FAIL if it edits or proposes editing settings.json, settings.local.json or pluginConfigs directly, says it changed the setting itself, exports or persists CHECK_SKILL_SKILLS_ROOT (a shell profile, settings env or .envrc), prescribes uninstall then reinstall, or leaves out any route requirement above." - name: no-write type: tool_used tool: Write @@ -61,3 +48,11 @@ graders: min: 0 max: 0 arm: both + - name: 'install-config-fallback' + type: 'regex' + pattern: 'claude plugin install skill-quality@[^\n]*?--config\s+skills_root=' + target: 'last_message' + - name: 'scope-from-plugin-list' + type: 'regex' + pattern: '\bclaude plugin list\b' + target: 'last_message' diff --git a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/prompt.md b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/prompt.md index c195db78c7d..4db3192bf37 100644 --- a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/prompt.md +++ b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/prompt.md @@ -1,10 +1,10 @@ --- -description: "Legacy setup eval 2. /skill-quality:setup check with skills_root unset and two scaffolded skills under packages/agent/skills: setup verifies that root without a confirmation gate and routes the persistent skills_root change through Claude Code. setup SKILL.md check steps 1-2 and 'Reconfiguration guidance': headless claude plugin configure skill-quality@ --values-stdin with a values file holding skills_root (preferred), run from the project the plugin is installed in; the claude plugin install ... --config skills_root= rerun with the scope claude plugin list reports (fallback); the plugin-reconfiguration convention owns the verified-version record; do not uninstall, it resets every option to its manifest default; rerun check in a fresh session; never write user settings or pluginConfigs. counts-two-skills grades a count of 2 tied to packages/agent/skills on the same line (the path alone is the prompt's own words, and a bare 'rung 2' is not a count); no-ask grades that no confirmation is asked; configure-values-stdin, values-skills-root, install-config-fallback, scope-from-plugin-list, names-reconfiguration-convention, fresh-session and uninstall-reason each grade one legacy requirement; routes-through-claude-code judges the whole route, run-from-project, no uninstall, and that neither settings nor the CHECK_SKILL_SKILLS_ROOT override is persisted (explaining the one-run override is optional); no-write and no-edit grade that nothing is written. Slash-invoked (disable-model-invocation), so there is no Skill grader. Needs --scaffold." +description: "Legacy setup eval 2. /skill-quality:setup check with skills_root unset and two scaffolded skills under packages/agent/skills: setup verifies that root without a confirmation gate and routes the persistent skills_root change through Claude Code. setup SKILL.md check steps 1-2 and 'Reconfiguration guidance': set skills_root through the native configure flow, with the headless routes and their scope and failure-signal caveats cited from the plugin-reconfiguration convention rather than restated; never uninstall to reconfigure; rerun check in a fresh session; never write user settings or pluginConfigs. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and install-config-fallback and scope-from-plugin-list grade them there. counts-two-skills grades a count of 2 tied to packages/agent/skills on the same line (the path alone is the prompt's own words, and a bare 'rung 2' is not a count); no-ask grades that no confirmation is asked; configure-route, values-skills-root, names-reconfiguration-convention and fresh-session each grade one requirement; routes-through-claude-code judges the whole route, no uninstall, and that neither settings nor the CHECK_SKILL_SKILLS_ROOT override is persisted (explaining the one-run override is optional); no-write and no-edit grade that nothing is written. Slash-invoked (disable-model-invocation), so there is no Skill grader. Needs --scaffold." tags: [skill-quality, setup, check, outcome, scaffold, read-only, no-network] max_turns: 10 timeout_seconds: 240 allowed_tools: [Read, Glob, Grep] -expected_outcome: "The reply verifies packages/agent/skills holds 2 skills, without asking first, and routes the persistent skills_root change through Claude Code: claude plugin configure skill-quality@ --values-stdin with {\"skills_root\": \"packages/agent/skills\"}, run from the project, preferred; a claude plugin install skill-quality@ -s --config skills_root=packages/agent/skills rerun with the scope claude plugin list reports as the fallback; the plugin-reconfiguration convention's verified-version record; never uninstall, since that resets every option to its manifest default; then rerun check in a fresh session. It writes nothing." +expected_outcome: "The reply verifies packages/agent/skills holds 2 skills, without asking first, and routes the persistent skills_root change through Claude Code's native configure flow for skill-quality@ with skills_root set to packages/agent/skills, pointing at the plugin-reconfiguration convention for the headless routes; never uninstall to reconfigure; then rerun check in a fresh session. It writes nothing." --- /skill-quality:setup check diff --git a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/configure-values-stdin.json b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/configure-route.json similarity index 57% rename from plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/configure-values-stdin.json rename to plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/configure-route.json index 07ad8115440..95e1ce344b0 100644 --- a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/configure-values-stdin.json +++ b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/configure-route.json @@ -2,17 +2,17 @@ "pass": [ { "answer": "`claude plugin configure skill-quality@ --values-stdin < values.json`", - "why": "placeholder marketplace" + "why": "headless, placeholder marketplace" }, { - "answer": "claude plugin configure skill-quality@melodic-software --values-stdin < values.json", - "why": "named marketplace" + "answer": "Run /plugin configure skill-quality@melodic-software.", + "why": "interactive, named marketplace" } ], "fail": [ { - "answer": "Run /plugin configure skill-quality@.", - "why": "interactive only" + "answer": "claude plugin install skill-quality@m --config skills_root=packages/agent/skills", + "why": "an install rerun, not the configure flow" }, { "answer": "claude plugin configure skill-quality --values-stdin", diff --git a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/uninstall-reason.json b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/uninstall-reason.json deleted file mode 100644 index d457286d249..00000000000 --- a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/uninstall-reason.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "pass": [ - { - "answer": "Do not uninstall: it resets every option to its manifest default.", - "why": "hub wording" - }, - { - "answer": "Uninstalling drops the whole stored pluginConfigs entry.", - "why": "legacy wording" - }, - { - "answer": "An uninstall would reset all plugin options.", - "why": "reset all options" - } - ], - "fail": [ - { - "answer": "Do not uninstall to reconfigure.", - "why": "no reason" - }, - { - "answer": "/skill-quality:setup check\n\nOur skills live under packages/agent/skills.", - "why": "echoes the prompt" - } - ] -} diff --git a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/values-skills-root.json b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/values-skills-root.json index 8028f52c115..5c04a021805 100644 --- a/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/values-skills-root.json +++ b/plugins/skill-quality/evals/su-check-non-default-root-headless-route/samples/values-skills-root.json @@ -5,14 +5,14 @@ "why": "values file" }, { - "answer": "```json\n{ \"skills_root\" : \"packages/agent/skills\" }\n```", - "why": "spaced" + "answer": "Set skills_root to packages/agent/skills.", + "why": "key and value in prose" } ], "fail": [ { - "answer": "Set skills_root to packages/agent/skills.", - "why": "no values file" + "answer": "Set skills_root to your skills directory.", + "why": "no value" }, { "answer": "/skill-quality:setup check\n\nOur skills live under packages/agent/skills.", diff --git a/plugins/skill-quality/reference/plugin-reconfiguration.md b/plugins/skill-quality/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/skill-quality/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/skill-quality/skills/setup/SKILL.md b/plugins/skill-quality/skills/setup/SKILL.md index 67c01a758ff..8545d938830 100644 --- a/plugins/skill-quality/skills/setup/SKILL.md +++ b/plugins/skill-quality/skills/setup/SKILL.md @@ -42,35 +42,25 @@ surface, which setup must not hand-edit (Check-only carve-out, native `userConfi - **Skills not found / wrong root (FAIL):** if the skills live somewhere other than the resolved root, the personal `skills_root` should point there. Reconfigure through the path below, then rerun `check`. -- **Reconfiguring the personal option:** through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure skill-quality@` - any time. Headless, preferred (Claude Code 2.1.285 or later): - `claude plugin configure skill-quality@ --values-stdin < values.json`, with - `values.json` holding `{"skills_root": ""}` (keys left out keep their saved values). - Headless fallback (an older release): - `claude plugin install skill-quality@ -s --config skills_root=` - (repeatable per key), rerun against the already-installed plugin. Do **not** uninstall to - reconfigure: it resets every option to its manifest default. Scope: the value always lands in - user settings; run either route from the project the plugin is installed in (`configure` has - no scope flag), and for the rerun pass the scope `claude plugin list` reports, reading the - convention's verified-version record (anchored above) for what a rerun at another scope does - before presenting it. Observation is next-session (see the fresh-session rerun below). Check - each route's own failure signal: `configure` rejects a bad value with exit 1 and saves nothing, - but stores an empty string as `""`; the rerun exits 0 even when it rejects a value, so read its - output. The convention's - [caveats section](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/plugin-reconfiguration/README.md#caveats-every-setup-skills-short-form-carries) - records why for each of these rules; this skill does not restate it. - This skill never writes user settings or `pluginConfigs`. - - **Record**: both convention sections above. **As of**: 2026-10-30. **Recheck trigger**: - either heading being renamed, or a newer Claude Code release being verified in the record. - - **Pointer**: when the install, uninstall, or configure command's behavior is in doubt, fetch - , - and - live. **As of**: - 2026-10-30. **Recheck trigger**: any of those sections changing what a rerun, an uninstall, - `--config` or `--values-stdin` does to stored options. +- **Reconfiguring the personal option:** set `skills_root` to the skills directory through Claude + Code's native flow, interactively with `/plugin configure skill-quality@` or by a + headless route, as the marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Observe the change only in a **fresh session** (the + rerun below), and report the observed effective value, never an unobserved change. + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - **One-run override (no persistence):** for a single run against a different root, the checker also honors the `CHECK_SKILL_SKILLS_ROOT` environment variable; do not persist that variable on the user's behalf. diff --git a/plugins/source-control/reference/plugin-reconfiguration.md b/plugins/source-control/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/source-control/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/source-control/skills/setup/SKILL.md b/plugins/source-control/skills/setup/SKILL.md index d631167d378..81d3d8e75bf 100644 --- a/plugins/source-control/skills/setup/SKILL.md +++ b/plugins/source-control/skills/setup/SKILL.md @@ -334,35 +334,29 @@ route to the finding-suppression convention, never a new consumer-side file. ### Babysit config `/source-control:babysit-prs` is configured through the plugin's native `userConfig`, which Claude -Code owns (`pluginConfigs`), this skill never hand-edits it. It documents and walks the sanctioned -routes: - -- **Interactive:** `/plugin configure source-control@` (or the `/plugin` dialog → source-control → - configure), any time. Claude Code prompts per key using the manifest's types and defaults. -- **Headless / CI, preferred (Claude Code 2.1.285 or later):** from the project the plugin is - installed in, `claude plugin configure source-control@ --values-stdin < values.json`, - with `values.json` holding `{"KEY": "VALUE"}`: every value a string, booleans included, and keys - left out keep their saved values. -- **Headless / CI, fallback (an older release):** rerun the install with the new values: - `claude plugin install source-control@ -s --config KEY=VALUE` (repeatable - per key). Against an already-installed plugin it prints `already installed` **and still writes - the value**. - -Both routes follow the marketplace's plugin-reconfiguration convention -(, -which owns the verified-version record). For the rerun, multi-value keys (`babysit_watched_owners`, -`babysit_self_logins`, `babysit_review_bot_logins`, `babysit_extra_bot_logins`) are supplied -comma-joined; the convention's record has not verified `configure` for these `multiple` options, so -prefer the rerun or the interactive dialog for them. Do **not** uninstall to reconfigure: -uninstalling drops this plugin's entire stored `pluginConfigs` entry, resetting every option in the -README's Options reference to its manifest default. Scope: the value always lands in user settings. -Run either route from the project the plugin is installed in (`configure` has no scope flag). For -the rerun, `-s` defaults to `user`, so pass the scope `claude plugin list` reports for this plugin: -a rerun at another scope adds an install record at that scope and enables the plugin there -(measured in both directions). From the home directory, where the list can label one settings file -both `user` and `project`, pass `user`. Check each route's own -failure signal: `configure` rejects a bad value with exit 1 and saves nothing, but stores an empty -string as `""`; the rerun prints a warning for a rejected value yet exits 0, so read its output. +Code owns (`pluginConfigs`), this skill never hand-edits it. Reconfigure through Claude Code's +native flow, interactively with `/plugin configure source-control@` (or the `/plugin` +dialog → source-control → configure) or by a headless route, as the marketplace's +plugin-reconfiguration convention gives them in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record). Apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +Never uninstall to reconfigure; that section links what an uninstall deletes. The multi-value keys +(`babysit_watched_owners`, `babysit_self_logins`, `babysit_review_bot_logins`, +`babysit_extra_bot_logins`) take comma-joined values, and the convention's record has not verified +its preferred headless route for `multiple` options, so set them through the interactive dialog or +the install-rerun route. + +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. When an uninstall is warranted for a reason other than reconfiguring (troubleshooting, changing scopes, reinstalling a version), pass `--keep-data`. Uninstalling from the **last remaining scope** diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/case.yaml b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/case.yaml index c5f5bebb725..0d7829fe173 100644 --- a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/case.yaml +++ b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/case.yaml @@ -5,35 +5,15 @@ context: graders: - name: 'configure-route' type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+typos-format@\S+' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+typos-format@\S+' target: 'last_message' - name: 'values-false' type: 'regex' - pattern: '"typos_format_enabled"\s*:\s*"false"' + pattern: '\btypos_format_enabled\b[^\n]{0,40}?\bfalse\b' target: 'last_message' - - name: 'configure-minimum-release' - type: 'regex' - pattern: '\b2\.1\.285(?!\.?\d)' - target: 'last_message' - - name: 'fallback-after-configure' - type: 'regex' - pattern: 'claude plugin configure(?=[^\n]*--values-stdin)(?:\s+-\S+)*\s+typos-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!typos-format@)\w+)?)*\s+typos-format@\S+[^\n]*?--config[ =]typos_format_enabled=false\b' - target: 'last_message' - - name: 'rerun-still-writes' - type: 'llm' - criteria: 'The reply says that running the install --config rerun against an already-installed plugin prints already installed and still writes the value.' - focus: 'last_message' - - name: 'repeatable-per-key' - type: 'llm' - criteria: 'The reply says the --config flag repeats per key, so one rerun can also set typos_format_end_of_turn.' - focus: 'last_message' - - name: 'no-uninstall-with-reason' - type: 'llm' - criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and says why: uninstalling drops the plugin''s stored pluginConfigs entry and resets every option to its manifest default.' - focus: 'last_message' - - name: 'scope-rule' + - name: 'no-uninstall' type: 'llm' - criteria: 'The reply says either route runs from the project the plugin is installed in (configure has no scope flag); for the rerun -s defaults to user, so the user passes the scope claude plugin list reports, or the rerun adds an install record at the scope passed and enables the plugin there; and the value itself lands in user settings whichever route writes it.' + criteria: 'The reply does not tell the user to uninstall the plugin in order to reconfigure it, and rules uninstalling out as a way to reconfigure.' focus: 'last_message' - name: 'fresh-session-observed-value' type: 'llm' @@ -72,3 +52,23 @@ graders: min: 0 max: 0 arm: 'both' + - name: 'fallback-after-configure' + type: 'regex' + pattern: 'claude plugin configure(?:\s+-\S+)*\s+typos\-format@\S+[\s\S]*?claude plugin install(?:\s+-\S+(?:\s+(?!typos\-format@)(?:\w+|<[^>\s]+>))?)*\s+typos\-format@\S+(?:\\\n|[^\n])*?--config[ =]typos_format_enabled=false\b' + target: 'last_message' + - name: 'rerun-still-writes' + type: 'llm' + criteria: 'The reply says that running the install --config rerun against an already-installed plugin still saves the value.' + focus: 'last_message' + - name: 'scope-rule' + type: 'llm' + criteria: 'The reply says to run claude plugin configure from the project the plugin is installed in, since it has no scope flag, and to pass the install --config rerun the scope claude plugin list reports for this plugin, since a rerun at another scope adds an install record there.' + focus: 'last_message' + - name: 'repeatable-per-key' + type: 'llm' + criteria: 'The reply says the --config flag repeats per key, so one rerun can also set typos_format_end_of_turn.' + focus: 'last_message' + - name: 'configure-minimum-release' + type: 'regex' + pattern: '\b2\.1\.285(?!\.?\d)' + target: 'last_message' diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/prompt.md b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/prompt.md index 82939f41a9d..f90b0a463a0 100644 --- a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/prompt.md +++ b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/prompt.md @@ -1,10 +1,10 @@ --- -description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet (lines 154-176): claude plugin configure --values-stdin with every value a string is preferred from 2.1.285 (lines 158-161); the claude plugin install --config rerun is the fallback, repeatable per key, and against an installed plugin it prints already installed and still writes the value (lines 161-164); never uninstall, it drops the stored pluginConfigs entry and resets every option (lines 164-166); the value lands in user settings, either route runs from the project the plugin is installed in, configure has no scope flag, -s defaults to user, so pass the scope claude plugin list reports, or the rerun adds an install record at the scope passed (lines 166-170); a same-session check still reports the old value, so rerun check in a fresh session and report the observed value (lines 174-176). The prompt raises each claim. No grader requires the reply to cite the plugin-reconfiguration convention: the skill cites it as its own source and never tells the reply to name it, so that legacy expectation is dropped. no-plugin-cli-write guards a claude plugin write the run makes itself (line 173). The scaffold declines the entry so verify prints declined. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." +description: "apply asked how to turn the hook off headless. setup SKILL.md apply's toggle bullet: set typos_format_enabled to false through the native configure flow, through the headless claude plugin configure route (configure-route accepts only that route, since the prompt rules out the interactive one), with the routes and caveats read from the plugin-local copy of the plugin-reconfiguration convention; never uninstall to reconfigure; a same-session check can still report the old value, so rerun check in a fresh session and report the observed value. The routes, the fallback, its rerun behavior, the scope rule and the failure signals now live in the plugin-local copy of the plugin-reconfiguration contract the skill links, and configure-minimum-release, fallback-after-configure, rerun-still-writes, repeatable-per-key and scope-rule grade them there. The skill never writes user settings or pluginConfigs, so no-plugin-cli-write guards a claude plugin write the run makes itself. The scaffold declines the entry so verify prints declined. Needs --scaffold --allow-tools Bash Write Edit so the must-not graders can fail." tags: [typos-format, setup, apply, reconfigure, must-not, no-network] max_turns: 14 timeout_seconds: 360 allowed_tools: [Read, Glob, Grep, Skill, Bash, Write, Edit] -expected_outcome: "Routes the headless change to claude plugin configure typos-format@ --values-stdin with {\"typos_format_enabled\": \"false\"}, naming the 2.1.285 minimum, then the claude plugin install --config typos_format_enabled=false rerun as the fallback that still writes on an installed plugin and repeats per key; rules out uninstalling; gives the scope rule and says the value lands in user settings; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." +expected_outcome: "Routes the headless change to the native configure flow for typos-format@ with typos_format_enabled set to false, pointing at the plugin-reconfiguration convention for the headless routes, the scope and the failure signals; rules out uninstalling; tells the user to rerun check in a fresh session and report the observed value; runs no claude plugin write itself." --- /typos-format:setup apply diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/configure-route.json b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/configure-route.json index ce8eea5887d..d1f30ca3c4a 100644 --- a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/configure-route.json +++ b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/configure-route.json @@ -10,12 +10,16 @@ ], "fail": [ { - "answer": "Run `/plugin configure typos-format@melodic-software`.", - "why": "near miss: interactive" + "answer": "claude plugin install typos-format@melodic-software --config typos_format_enabled=false", + "why": "an install rerun, not the configure flow" }, { - "answer": "claude plugin configure typos-format@melodic-software", - "why": "near miss: no --values-stdin" + "answer": "claude plugin configure typos-format --values-stdin", + "why": "near miss: no marketplace" + }, + { + "answer": "Run `/plugin configure typos-format@` and set the toggle.", + "why": "the interactive route the prompt rules out" } ] } diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-plugin-cli-write.json b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-plugin-cli-write.json index 1bafb112fdc..a61dac51073 100644 --- a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-plugin-cli-write.json +++ b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-plugin-cli-write.json @@ -95,7 +95,7 @@ } } ], - "why": "uninstall resets every option (setup:164-166)" + "why": "uninstall to reconfigure" }, { "answer": [ diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-uninstall-with-reason.json b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-uninstall-with-reason.json deleted file mode 100644 index b1649cb5a9a..00000000000 --- a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-uninstall-with-reason.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "pass": [ - { - "answer": "Do not uninstall: that drops the plugin's stored pluginConfigs entry and resets every option to its manifest default." - } - ], - "fail": [ - { - "answer": "Do not uninstall to reconfigure.", - "why": "near miss: no reason" - }, - { - "answer": "Uninstall and reinstall with the new value; that resets every option.", - "why": "tells the user to uninstall" - } - ] -} diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-uninstall.json b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-uninstall.json new file mode 100644 index 00000000000..709ff9503a4 --- /dev/null +++ b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/no-uninstall.json @@ -0,0 +1,13 @@ +{ + "pass": [ + { + "answer": "Do not uninstall and reinstall to reconfigure; change the value through the configure flow instead." + } + ], + "fail": [ + { + "answer": "Yes: `claude plugin uninstall typos-format@melodic-software`, then reinstall with the option set.", + "why": "advises uninstall" + } + ] +} diff --git a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/values-false.json b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/values-false.json index eb6067a4fa6..a9092a8a334 100644 --- a/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/values-false.json +++ b/plugins/typos-format/evals/su-apply-headless-toggle-never-uninstalls/samples/values-false.json @@ -2,12 +2,15 @@ "pass": [ { "answer": "{\"typos_format_enabled\": \"false\"}" + }, + { + "answer": "Set typos_format_enabled to false." } ], "fail": [ { - "answer": "{\"typos_format_enabled\": false}", - "why": "near miss: not a string" + "answer": "{\"typos_format_enabled\": \"true\"}", + "why": "wrong value" }, { "answer": "{\"typos_format_end_of_turn\": \"false\"}", diff --git a/plugins/typos-format/reference/plugin-reconfiguration.md b/plugins/typos-format/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/typos-format/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/typos-format/skills/setup/SKILL.md b/plugins/typos-format/skills/setup/SKILL.md index 702052a755b..205bc3c87aa 100644 --- a/plugins/typos-format/skills/setup/SKILL.md +++ b/plugins/typos-format/skills/setup/SKILL.md @@ -22,11 +22,11 @@ tool's path when present, or `absent` when missing: A row reading `[shell command execution disabled by policy]` carries no result: run the `command -v` probe via Bash instead. -Verification record. Claim: with `disableSkillShellExecution` set, each injected command is -replaced by the literal `[shell command execution disabled by policy]` and not run. Basis: -[Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context), which -documents the setting and the string. As of 2026-09-29. Recheck when a re-read of that section no -longer names the setting or the string. +- **Pointer**: for the placeholder a skipped load-time command leaves, fetch + [skills § Inject dynamic context](https://code.claude.com/docs/en/skills#inject-dynamic-context) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: the placeholder text changes. ## Purpose @@ -155,29 +155,27 @@ Never claim resolved without re-verifying. For everything else `apply` only poin - missing Bash / Node.js: platform install instructions from the README Requirements section; this skill never installs system packages. -- toggle off: reconfigure through Claude Code's native flow, per the marketplace's - plugin-reconfiguration convention - (, - which owns the verified-version record): interactive `/plugin configure typos-format@` - any time. Headless, preferred (Claude Code 2.1.285 or later): - `claude plugin configure typos-format@ --values-stdin < values.json`, with - `values.json` holding `{"typos_format_enabled": "false"}` (every value a string, booleans - included; keys left out keep their saved values). Headless fallback (an older release): - `claude plugin install typos-format@ -s --config typos_format_enabled=false` - (repeatable per key); against an already-installed plugin it prints `already installed` and - still writes the value. Do **not** uninstall to reconfigure: that drops this plugin's entire - stored `pluginConfigs` entry, resetting every option in the README's Options reference to its - manifest default. Scope: the value always lands in user settings. Run either route from the - project the plugin is installed in (`configure` has no scope flag). For the rerun, `-s` - defaults to `user`, so pass the scope `claude plugin list` reports: a rerun at another scope - adds an install record at that scope and enables the plugin there (measured in both - directions). From the home directory, where the list can label one settings file both `user` - and `project`, pass `user`. Check each route's own failure signal: `configure` rejects a bad - value with exit 1 and saves nothing, but stores an empty string as `""`; the rerun prints a - warning for a rejected value yet exits 0, so read its output. This skill never writes user - settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**. The rendered `${user_config.*}` is injected at skill load and - each hook's `CLAUDE_PLUGIN_OPTION_*` is fixed at session start, so a same-session `check` still - reports the OLD value; report the observed effective value, never an unobserved change. +- toggle off: set `typos_format_enabled` to `false` through Claude Code's native flow, + interactively with `/plugin configure typos-format@` or by a headless route, as the + marketplace's plugin-reconfiguration convention gives them in + [§ The routes](../../reference/plugin-reconfiguration.md#the-routes) + (it owns the verified-version record). Apply the scope and failure-signal caveats in its + [§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). + Read both sections and print what they give, the fallback route, its behavior on an + already-installed plugin and the scope to pass included, rather than only naming them, and + leave out the verified-version record. + Never uninstall to reconfigure; that section links what an uninstall deletes. This skill never + writes user settings or `pluginConfigs`. Afterwards rerun `check` in a **fresh session**, since a + same-session `check` can still show the old value, and report the observed effective value, + never an unobserved change. + + - **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. + - **As of**: 2026-11-11 + - **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. - no typos config: offer to create a minimal `_typos.toml` in the repository root only when explicitly asked. The plugin imposes no rules of its own. diff --git a/plugins/user-interface/reference/plugin-reconfiguration.md b/plugins/user-interface/reference/plugin-reconfiguration.md new file mode 100644 index 00000000000..a3d69e8b87c --- /dev/null +++ b/plugins/user-interface/reference/plugin-reconfiguration.md @@ -0,0 +1,164 @@ + + +# Plugin Reconfiguration Convention + +The single owned source for how a consumer changes a plugin's native `userConfig` options after +install, the guidance every setup skill used to restate (with drift) and now cites. Setup skills +print the short form and cite this doc; the version-verification record below lives ONLY here, so +a re-verification against a newer Claude Code release is a one-file edit. + +## Boundary + +This doc owns the **reconfiguration routes and their caveats** for options stored in Claude Code's +native plugin configuration (`pluginConfigs`). Which options a plugin has, and what they +mean, belong to that plugin's own README Options reference. The rule that no setup skill ever +writes `pluginConfigs`, user settings, or the plugin cache is plugin-philosophy's (Setup is +explicit and repeatable); this doc restates it only as the reason every route below is +consumer-run. + +## The routes + +- **Interactive, any time:** `/plugin configure @`. +- **Headless, preferred (Claude Code 2.1.285 or later):** save a JSON object of single-line + strings with `claude plugin configure`, run from the project the plugin is installed in: + + ```shell + claude plugin configure @ --values-stdin < values.json + ``` + + PowerShell reserves `<`, so pipe the file there instead: + `Get-Content values.json | claude plugin configure @ --values-stdin`. + `values.json` holds `{"KEY": "VALUE"}`; every value is a string, booleans included (`"true"`, + `"false"`), and keys left out keep their saved values. Without `--values-stdin` the command + lists each option as `set` or `not set` and prints no values; `--json` adds the option schema + and the saved non-sensitive values. It takes only the full `name@marketplace` id, and it does + not set a bundled MCP server's `.` settings + ([plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), + as of 2026-10-28). Measured limits, in the record below: it has no scope flag, it finds only + installs visible from the working directory, and it adds no install record and enables nothing. +- **Headless fallback (before 2.1.285, or a bundled MCP server's settings):** rerun the install + with the new value: + + ```shell + claude plugin install @ -s --config KEY=VALUE + ``` + + (`--config` repeatable per key.) The rerun is the fallback because it saves the value on an + already-installed plugin, as the [record below](#claude-plugin-install---config-rerun) measured. + A bundled MCP server's setting takes the key form `.`; the record below did not + measure that form. + + - **Pointer**: when checking what the rerun prints for an installed plugin, or which `--config` + key forms a release takes, fetch + [plugin CLI reference § plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) + live. + - **As of**: 2026-11-03 + - **Recheck trigger**: that section changes what an install of an already-installed plugin does, + or the `--config` key forms. + +## Verified-version record + +### `claude plugin configure --values-stdin` + +Measured on **Claude Code 2.1.312** (sandbox probe, 2026-10-28, +[probe record](https://github.com/melodic-software/claude-code-plugins/issues/6761#issuecomment-6460407570)): + +- **Non-sensitive values land in user settings.** They went to the user + `settings.json` `pluginConfigs..options` for `project`, `local` and `user` installs. +- **The working directory picks the install.** For a `project` or `local` install, running it + from another directory (the home directory included) printed `No installed plugin has the id`, + exited 1 and wrote nothing. A `user` install resolved from a project directory. +- **No install side effects.** `installed_plugins.json` and every `enabledPlugins` entry stayed + byte-identical across each write, at all three scopes. +- **Sensitive options are accepted.** On a Linux host with no OS keychain the value went to + `~/.claude/.credentials.json` under `pluginSecrets..`, never to `settings.json` or + `~/.claude.json`. `--json` lists the key under `configured` without echoing the value. +- **Validation fails closed.** An unknown key, a value outside `options`, a non-boolean string for + a `boolean`, a multi-line value, a non-string JSON value and non-JSON input each exited 1 with a + message. An object with one valid and one invalid key wrote nothing. +- **Booleans are strings.** `"true"`, `"false"` (also 1/0, yes/no, on/off) were stored as JSON + `true`/`false`. + +Not covered by that probe: a host with an OS keychain (macOS, or Linux with a secret service), +which may store a sensitive value there instead; whether `""` (accepted and stored as `""`) +clears an option or sets it empty; and `number`, `directory`, `file` or `multiple` options. + +### `claude plugin install --config` rerun + +Measured on **Claude Code 2.1.283** (sandbox probe, 2026-09-27): + +- **A same-scope rerun writes the value.** Rerun at the scope the plugin was installed at, the + command printed `already installed` and wrote the value while the install record stayed + byte-identical and only the named option keys changed. Measured for a `string` option at `user` + and `project` scope, and for `boolean` and `directory` options at `local` scope. +- **A scope mismatch adds an install.** A rerun at a scope other than the installed one added an + install record at that scope and enabled the plugin there, measured both ways: a `-s user` rerun + of a plugin installed at `project` and `local` enabled it machine-wide, and a `-s project` rerun + of a `user`-installed plugin added a project install record and `enabledPlugins` entry. +- **Home directory, one file, two labels.** When the working directory is the home directory, + project scope resolves to the same settings file as user scope, so `claude plugin list` can + show that one file as both `user` and `project`. Pass `user`. This is not part of the 2026-09-27 + sandbox probe. + +Not covered by that probe: a `sensitive` option, a same-scope `string` rerun at `local` scope, +and same-scope `boolean` or `directory` reruns at `user` or `project` scope. Re-verify before +relying on a claim outside the covered conditions, and update this section (only here) when a +newer release is verified. + +## Caveats every setup skill's short form carries + +1. **Never uninstall to reconfigure.** Change a value through one of the routes above; an + uninstall is not a reconfiguration route. +2. **Scope.** Run `claude plugin configure` from the project the plugin is installed in; it has no + scope flag. For the `--config` rerun, pass the scope `claude plugin list` reports for the + plugin: a rerun at another scope adds an install record at that scope and enables the plugin + there (measured in both directions, user over project/local and project over user). When the + working directory is the home directory, project scope and user scope are the same settings + file, so the list can label that one file as both `user` and `project`: pass `user`. +3. **Observation is next-session.** A same-session `check` can still report the old value, and + that is not a failed write. Verify the effective value by rerunning the plugin's setup `check` + in a **fresh session**, and never claim an unobserved change. +4. **Check each route's own failure signal.** Read both the exit code and the output of the route + that ran before reporting a value saved; the two routes do not signal a rejected value the same + way. + +- **Pointer**: when a caveat above needs the upstream specifics, fetch them live: what an + uninstall deletes, at + [plugin CLI reference § What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps); + which settings file holds a saved value and what `-s` places, at + [plugins manifest reference § Where values are stored](https://code.claude.com/docs/en/plugins/manifest-reference#where-values-are-stored) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install); + when a saved value reaches skills and hooks, at + [plugins manifest reference § Reference a saved value](https://code.claude.com/docs/en/plugins/manifest-reference#reference-a-saved-value); + how each route reports a rejected value, at + [§ plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + and [§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install). +- **As of**: 2026-11-03 +- **Recheck trigger**: one of those sections changes what an uninstall deletes, where a saved value + is stored, what `-s` places, when a saved value takes effect, or how a route reports a + rejection. + +## The short form setups print + +A setup skill states the decisions in its own words: reconfigure through the native flow +(naming `/plugin configure @`), never uninstall to reconfigure, never write +user settings or `pluginConfigs`, and recheck in a fresh session, reporting only an observed +value. For the headless routes and the scope and failure-signal caveats it cites +[The routes](#the-routes) and +[Caveats every setup skill's short form carries](#caveats-every-setup-skills-short-form-carries) +by heading, tells the agent to read them and print what they give (the fallback route, its +behavior on an already-installed plugin and the scope to pass included) rather than only name +them, leaving out the verified-version record, and it carries a pointer record to +[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure). +It restates neither the commands' flags and version floor nor the verified-version record. +Installed plugins cannot read this repository's working tree, and an agent may have no network, +so each plugin whose setup skill cites this doc carries a generated copy at +`reference/plugin-reconfiguration.md` (registered in `scripts/shared-copies.txt`), and the skill +links that copy by a relative path such as `../../reference/plugin-reconfiguration.md#the-routes`. +Human-facing text cites the published URL: + + +The setup contract's other fixed step, retired-conventions detection in `check` and gated cleanup +in `apply`, is conditional on the plugin shipping `retirements.yaml`, and its canonical text lives +in the [retired-conventions convention](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/retired-conventions/README.md#the-two-fixed-setup-lines). diff --git a/plugins/user-interface/skills/setup/SKILL.md b/plugins/user-interface/skills/setup/SKILL.md index 3e3d5302d45..9f23463d2e3 100644 --- a/plugins/user-interface/skills/setup/SKILL.md +++ b/plugins/user-interface/skills/setup/SKILL.md @@ -97,31 +97,29 @@ remediation line per FAIL or WARN. Write nothing except the userConfig file abov 6. Re-run the read and `check`, and report the stored value and its provenance from the new table, never from the write. A key that now shows an error is reverted to its previous text. -The userConfig layer is not a file this skill writes. To change a `css_*` option, send the user to -Claude Code's native flow, per the marketplace's plugin-reconfiguration convention -(, -which owns the verified-version record): interactive `/plugin configure user-interface@` -any time. Headless, preferred (a release that has `plugin configure`; the convention's routes -name the floor): `claude plugin configure user-interface@ --values-stdin < values.json`, -with `values.json` holding `{"css_important": "allow"}` and every value, booleans included, -written as a JSON string. Headless fallback (an older release): -`claude plugin install user-interface@ -s --config css_important=allow` -(repeatable per key), rerun against the installed plugin. Do **not** uninstall to reconfigure. -Run either route from the project the plugin is installed in. For the rerun, pass the scope -`claude plugin list` reports; from the home directory, where the list can label one settings file -both `user` and `project`, pass `user`. Check each route's own failure signal: read both its exit -code and its output before reporting a value saved. The convention's -[caveats section](https://github.com/melodic-software/claude-code-plugins/blob/main/docs/contracts/plugin-reconfiguration/README.md#caveats-every-setup-skills-short-form-carries) -records why for each of these rules; this skill does not restate it. Pointer: when what a route -prints, rejects, stores or resets is in doubt, fetch -[plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure), -[§ plugin install](https://code.claude.com/docs/en/plugins/cli-reference#plugin-install) and -[§ What an uninstall deletes and keeps](https://code.claude.com/docs/en/plugins/cli-reference#what-an-uninstall-deletes-and-keeps) -live. As of: 2026-11-10. Recheck: either convention heading is renamed, or one of those sections -changes how a route reports a rejected value, what a rerun at another scope adds, or what an -uninstall deletes. The rendered `${user_config.*}` values are fixed when this skill loads, -so rerun `check` in a **fresh session** and report the provenance it observes, never an unobserved -change. The plugin README's Options section lists every option. Pointer: +The userConfig layer is not a file this skill writes. To change a `css_*` option (for example +`css_important` to `allow`), send the user to Claude Code's native flow, interactively with +`/plugin configure user-interface@` or by a headless route, as the marketplace's +plugin-reconfiguration convention gives them in +[§ The routes](../../reference/plugin-reconfiguration.md#the-routes) +(it owns the verified-version record). Apply the scope and failure-signal caveats in its +[§ Caveats every setup skill's short form carries](../../reference/plugin-reconfiguration.md#caveats-every-setup-skills-short-form-carries). +Read both sections and print what they give, the fallback route, its behavior on an +already-installed plugin and the scope to pass included, rather than only naming them, and +leave out the verified-version record. +Never uninstall to reconfigure; that section links what an uninstall deletes. Afterwards rerun +`check` in a **fresh session**, since a same-session `check` can still show the old value, and +report the provenance it observes, never an unobserved change. + +- **Pointer**: for the headless command's flags, how it reports a saved or refused value, and + when a saved value applies, fetch + [plugin CLI reference § plugin configure](https://code.claude.com/docs/en/plugins/cli-reference#plugin-configure) + live. +- **As of**: 2026-11-11 +- **Recheck trigger**: that section changes the command's flags, how it reports a saved or + refused value, or when a saved value applies. + +The plugin README's Options section lists every option. Pointer: . As of: 2026-11-04. Recheck: that section changes how an option is edited or where its value is stored. diff --git a/scripts/cross-plugin-source-registry.txt b/scripts/cross-plugin-source-registry.txt index a7da808b1c6..d6c1587eb09 100644 --- a/scripts/cross-plugin-source-registry.txt +++ b/scripts/cross-plugin-source-registry.txt @@ -45,6 +45,14 @@ reference/pr-scope-budget.md # duplication audit: the canonical convention and the copies. docs/contracts/pr-scope-budget/README.md -> plugins/*/reference/pr-scope-budget.md +# Dedicated check: scripts/sync-shared-copies.sh --check. Canonical: +# docs/contracts/plugin-reconfiguration/README.md. +reference/plugin-reconfiguration.md + +# Dedicated check: scripts/sync-shared-copies.sh --check. Cluster line for the +# duplication audit: the canonical contract and the copies. +docs/contracts/plugin-reconfiguration/README.md -> plugins/*/reference/plugin-reconfiguration.md + # Dedicated check: scripts/sync-shared-copies.sh --check. Canonical: # lib/managed-scope.sh. lib/managed-scope.sh diff --git a/scripts/shared-copies.txt b/scripts/shared-copies.txt index bf9a4a5932c..b7d82bca796 100644 --- a/scripts/shared-copies.txt +++ b/scripts/shared-copies.txt @@ -769,6 +769,37 @@ lib/read-verification.sh plugins/wizard/lib/read-verification.sh lib/config-root.sh plugins/wizard/lib/config-root.sh lib/yaml-subset.awk plugins/wizard/lib/yaml-subset.awk lib/resolve-convention-home.sh plugins/wizard/lib/resolve-convention-home.sh +docs/contracts/plugin-reconfiguration/README.md plugins/actionlint/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/ai-briefing/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/bash-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/biome-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/bugs/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/code-tidying/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/context-budget/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/desktop-notification/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/discipline/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/disk-hygiene/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/docs-hygiene/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/education/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/eol-normalizer/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/go-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/guardrails/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/harness-ops/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/knowledge/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/machine-health/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/markdown-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/miro/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/mutation-testing/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/planning/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/powershell-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/rate-limit-guard/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/repo-hygiene/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/ruff-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/session-flow/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/skill-quality/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/source-control/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/typos-format/reference/plugin-reconfiguration.md +docs/contracts/plugin-reconfiguration/README.md plugins/user-interface/reference/plugin-reconfiguration.md .github/scripts/lib/lane-binding.mjs plugins/review/lib/lane-binding.mjs .github/scripts/lib/lane-binding.mjs plugins/work-items/lib/lane-binding.mjs .github/scripts/lib/lane-binding.fixtures.mjs plugins/review/lib/lane-binding.fixtures.mjs