Skip to content

fix(claude): preserve hooks and includeCoAuthoredBy#1916

Open
zerone0x wants to merge 1 commit intofarion1231:mainfrom
zerone0x:fix/claude-preserve-hooks-1907
Open

fix(claude): preserve hooks and includeCoAuthoredBy#1916
zerone0x wants to merge 1 commit intofarion1231:mainfrom
zerone0x:fix/claude-preserve-hooks-1907

Conversation

@zerone0x
Copy link
Copy Markdown

@zerone0x zerone0x commented Apr 6, 2026

Summary / 概述

When syncing Claude providers, preserve existing hooks and includeCoAuthoredBy values from the current ~/.claude/settings.json if the provider config does not define them. This prevents those fields from being wiped during node/provider switches.

Related Issue / 关联 Issue

Fixes #1907

Screenshots / 截图

Before / 修改前 After / 修改后
N/A N/A

Checklist / 检查清单

  • pnpm typecheck passes / 通过 TypeScript 类型检查
  • pnpm format:check passes / 通过代码格式检查
  • cargo clippy passes (if Rust code changed) / 通过 Clippy 检查(如修改了 Rust 代码)
  • Updated i18n files if user-facing text changed / 如修改了用户可见文本,已更新国际化文件

Fixes farion1231#1907

Co-Authored-By: Claude <noreply@anthropic.com>
@farion1231
Copy link
Copy Markdown
Owner

Hi, I owe you an apology for the long silence. As the sole maintainer of this project, I've been overwhelmed and fell behind on reviews — but that's on me, not on you. Your contribution matters and I will review this PR. If it needs a rebase, just let me know or I can handle it. Thank you for your time and patience.

@farion1231
Copy link
Copy Markdown
Owner

farion1231 commented Apr 8, 2026

Hi @zerone0x, thanks for taking the time to look into #1907 and for the clean, well-scoped PR. Unfortunately I don't think this direction is the right fix — let me explain the reasoning so we're on the same page.

Why a hardcoded field whitelist isn't the fix we want

cc-switch treats each provider as a complete, self-contained snapshot of settings.json. Switching intentionally replaces the whole file, not just env. For fields that should follow the user across providers (like hooks, includeCoAuthoredBy, and similar), we already have a dedicated mechanism: the Shared Config Snippet, which is stored separately and deep-merged into whichever provider gets written to live. includeCoAuthoredBy: false is in fact the default content of that snippet (DEFAULT_COMMON_CONFIG_SNIPPET in src/components/providers/forms/hooks/useCommonConfigSnippet.ts).

Layering a hardcoded ["hooks", "includeCoAuthoredBy"] fallback on top of that creates several problems:

  1. Overlaps with the Shared Config layer — these two fields would end up handled by two mechanisms at once. A user who deliberately customizes includeCoAuthoredBy via the shared snippet and then sets it differently on one provider would silently get the old value back from the live file.
  2. Breaks the "full snapshot" semantics — a provider can no longer be reasoned about in isolation; its effective on-disk config becomes a function of whatever happens to be in the live file at write time.
  3. Removes the ability to deliberately drop a field — if a user intentionally removes hooks from one provider ("this account should not run any hooks"), the new logic silently restores it from live. There's no way to express "I actually want this field gone".
  4. Doesn't scale — the same ask will keep coming back for other fields: permissions, apiKeyHelper, model, statusLine, etc. A hardcoded whitelist can't keep up, and each addition blurs the ownership of that field further.
  5. Earlier field-merge experiments already showed this to be fragile — we've tried variations of field-level preservation before and hit problems with nested arrays (hooks itself is one), "user removed vs user never set" ambiguity, and multi-layer merge conflicts. The current "full snapshot + explicit shared config" model was the deliberate outcome of that experience.

The intended fix for #1907

The recommended path is already documented in the README FAQ ("My plugin configuration disappeared after switching providers"): extract hooks / includeCoAuthoredBy into the Shared Config Snippet, and make sure "Write Shared Config" is enabled on each Claude provider. I posted a detailed walkthrough on #1907 — feel free to take a look there.

If you'd still like to contribute in this area

A couple of directions that would be a good fit, if you're interested:

Either of those I'd be happy to review. Thanks again for digging in — the investigation and test you added were solid, it's just that the approach conflicts with the design direction we want to stick with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] hooks in ~/.claude/settings.json get randomly deleted when switching nodes

2 participants