Repository navigation
Conversation
`specify extension add`, `remove`, `enable`, `disable` and `update`,
`specify init` with extensions, and integration setup all refresh
runtime events. With no events configured, the refresh takes
install_integration_events()'s empty-map branch into
_remove_native_event_hooks(), and its JSON removal helpers rewrote the
agent's native config even when it held no Specify-owned entries.
In a plain Claude project (likewise Gemini, Qwen, Tabnine, Cursor,
Copilot, Devin and opencode), adding an extension that declares no
events re-indented the user's .claude/settings.json or opencode.json,
dropped empty values such as "hooks": {"Notification": []} or
"plugin": [], and deleted a config whose whole content was {}.
github#4563 fixed the same defect for Codex's TOML in _remove_toml_entries().
Give the JSON helpers the matching guard: _remove_json_entries(),
_remove_copilot_entries() and _remove_json_root_entries() return early
when no Specify-marked entry is present, and _remove_opencode_entries()
returns early when opencode.json does not reference the Spec Kit plugin.
Files that do hold owned entries are still stripped, rewritten or
deleted as before.
Assisted-by: Claude Code (model: Claude Opus 5.5, autonomous)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Many commands refresh runtime events:
specify extension add/remove/enable/disable/update,specify initwith extensions, and integration setup. When no events are configured, that refresh rewrites the agent's native JSON config even though it holds nothing Spec Kit owns. Affected files include.claude/settings.jsonandopencode.json; the Gemini, Qwen, Tabnine, Cursor, Copilot and Devin configs go through the same JSON helpers. The rewrite:"hooks": {"Notification": []}or"plugin": []{}#4563 / #4564 fixed exactly this for Codex's TOML (
_remove_toml_entries()). The JSON helpers never got the same guard.Reproduction in a plain Claude project, adding the bundled
selftestextension (which declares no events):Root cause:
install_integration_events()calls_remove_native_event_hooks()._remove_json_entries(),_remove_copilot_entries(),_remove_json_root_entries()and_remove_opencode_entries().The fix:
_has_marked_entry()helper on top of the existing_has_marker(), which already recurses into nested matcher-grouphooks._remove_opencode_entries()returns early whenopencode.jsondoesn't reference the Spec Kit plugin.Vibe is deliberately left out because open PR #4646 rewrites Vibe's merge and removal.
Testing
uv run specify --helpuv sync && uv run pytestmainand on this branchNew
TestJsonNoOpRemovalintests/specify_cli/events/test_events.py, each case parametrized overclaude,copilot,cursor-agent,devinandopencode:test_no_events_leaves_user_config_untouched: the bytes are unchangedtest_no_events_keeps_empty_user_config: a{}file is keptOn
main, the first two cases fail for every key: 10 failures, e.g.assert b'{\n "hooks...' == b'{\n\t"hooks...'andFileNotFoundError: .../.claude/settings.json. The positive cases pass. With the change the file gives 161 passed.tests/test_*_python_parity.pyfiles occur identically on unmodifiedmainon this machine under full-suite load; those files pass when run on their own (257 passed, 112 skipped).uvx ruff@0.15.0 check src tests: cleanAI Disclosure
AI disclosure: Claude Code (model: Claude Opus 5.5, max reasoning effort, autonomous agent mode) opened this PR on behalf of @yxshee. A Claude Code subagent (same model, autonomous) found the bug and wrote the fix and tests. The lead agent independently re-ran the new tests against
mainand the branch, reproduced the behavior above with the real CLI, reviewed the diff, and wrote this description. No human reviewed the change line by line before it was opened.🤖 Generated with Claude Code