Summary
repowise init rewrites ~/.claude/settings.json with a minimal stub
(mcpServers.repowise + the two PreToolUse/PostToolUse hooks) whenever it
can't parse the existing file as JSON, silently discarding every other key
the user had configured — permissions, enabledPlugins, extraKnownMarketplaces,
other mcpServers, other hooks, and so on.
Because any transient or unrelated JSON issue (stray trailing comma, a comment,
a partial write from another tool, a BOM, an encoding mismatch, etc.) trips
the same path, this is real user data loss triggered by something outside
init's control. On one run I watched ~/.claude/settings.json go from 178
lines to 42 lines: all permissions, 24 enabled plugins, 6 known marketplaces,
and an unrelated MCP server entry were erased.
Reproduction
-
Have a ~/.claude/settings.json with any meaningful content.
-
Introduce any parse error — e.g. a trailing comma before a }:
"hooks": {
"PreCompact": [ ... ],
},
-
Run repowise init <any repo> --index-only -y.
-
Observe ~/.claude/settings.json: everything except mcpServers.repowise
and the PreToolUse/PostToolUse hooks is gone. No warning, no backup,
exit code 0.
Root cause
packages/cli/src/repowise/cli/mcp_config.py — three call sites all share
the same silent-fallback pattern:
_merge_mcp_entry (≈line 93):
try:
existing = json.loads(config_path.read_text(encoding="utf-8"))
except (json.JSONDecodeError, OSError):
existing = {}
install_claude_code_hooks (≈line 176): same idiom.
save_root_mcp_config (≈line 72): same idiom (less catastrophic
because it writes a project-local .mcp.json rather than the global
settings file, but same anti-pattern).
In every case, after swallowing the parse error the code proceeds to
settings_path.write_text(json.dumps(existing, indent=2) + "\n"), which
overwrites the user's file with the empty base plus whatever entry
init wanted to add.
This pattern directly violates the project's own documented blocker rule
"Silent fallbacks — raise or log, don't quietly default."
Recommended fix
Do not treat a parse failure on an existing file as "no prior config."
Two acceptable strategies:
- Abort and report: raise / log an explicit error pointing at the
settings path, tell the user what failed to parse, and exit without
writing. Let the user resolve the file by hand.
- Backup and warn: copy the unparseable file to
settings.json.repowise-backup-<timestamp> and emit a visible warning
on stderr before writing the new content.
A missing file is still fine to treat as {} — the hazard is specifically
the FileExists && ParseFails branch.
The same fix should apply to all three call sites, ideally via a shared
helper so the behavior can't drift again.
Adjacent smells (not part of this bug, flagging for context)
- The
PreToolUse/PostToolUse hooks install repowise augment as a bare
command name. If the user's PATH doesn't resolve repowise to the uv-installed
binary with the augment subcommand, every Grep/Glob/Bash tool call in
Claude Code spams a No such command 'augment' error until the hooks
are manually removed.
install_claude_code_hooks writes the repowise MCP server into the
global settings.json pointing at the target repo — every
init <dir> invocation rebinds the user's global MCP to the last-indexed
path. A per-project .mcp.json (already produced by
save_root_mcp_config) would avoid the pollution.
Both are worth a separate look; happy to file follow-ups if useful.
Severity
High — silent user-config destruction. No warning, no backup, no exit code.
Recoverable only if the user has shell history / a backup / version
control on their dotfiles.
Note from the reporter
This was found while working on
#89 , and TBH, Claude either
made a whoopsie while working on the PR, oooooor the whoopsie was already
there. Either way, having found this while working on it wasn't... fun.
Summary
repowise initrewrites~/.claude/settings.jsonwith a minimal stub(
mcpServers.repowise+ the twoPreToolUse/PostToolUsehooks) whenever itcan't parse the existing file as JSON, silently discarding every other key
the user had configured — permissions,
enabledPlugins,extraKnownMarketplaces,other
mcpServers, otherhooks, and so on.Because any transient or unrelated JSON issue (stray trailing comma, a comment,
a partial write from another tool, a BOM, an encoding mismatch, etc.) trips
the same path, this is real user data loss triggered by something outside
init's control. On one run I watched~/.claude/settings.jsongo from 178lines to 42 lines: all permissions, 24 enabled plugins, 6 known marketplaces,
and an unrelated MCP server entry were erased.
Reproduction
Have a
~/.claude/settings.jsonwith any meaningful content.Introduce any parse error — e.g. a trailing comma before a
}:Run
repowise init <any repo> --index-only -y.Observe
~/.claude/settings.json: everything exceptmcpServers.repowiseand the
PreToolUse/PostToolUsehooks is gone. No warning, no backup,exit code 0.
Root cause
packages/cli/src/repowise/cli/mcp_config.py— three call sites all sharethe same silent-fallback pattern:
_merge_mcp_entry(≈line 93):install_claude_code_hooks(≈line 176): same idiom.save_root_mcp_config(≈line 72): same idiom (less catastrophicbecause it writes a project-local
.mcp.jsonrather than the globalsettings file, but same anti-pattern).
In every case, after swallowing the parse error the code proceeds to
settings_path.write_text(json.dumps(existing, indent=2) + "\n"), whichoverwrites the user's file with the empty base plus whatever entry
initwanted to add.This pattern directly violates the project's own documented blocker rule
"Silent fallbacks — raise or log, don't quietly default."
Recommended fix
Do not treat a parse failure on an existing file as "no prior config."
Two acceptable strategies:
settings path, tell the user what failed to parse, and exit without
writing. Let the user resolve the file by hand.
settings.json.repowise-backup-<timestamp>and emit a visible warningon stderr before writing the new content.
A missing file is still fine to treat as
{}— the hazard is specificallythe
FileExists && ParseFailsbranch.The same fix should apply to all three call sites, ideally via a shared
helper so the behavior can't drift again.
Adjacent smells (not part of this bug, flagging for context)
PreToolUse/PostToolUsehooks installrepowise augmentas a barecommand name. If the user's PATH doesn't resolve
repowiseto the uv-installedbinary with the
augmentsubcommand, every Grep/Glob/Bash tool call inClaude Code spams a
No such command 'augment'error until the hooksare manually removed.
install_claude_code_hookswrites therepowiseMCP server into theglobal
settings.jsonpointing at the target repo — everyinit <dir>invocation rebinds the user's global MCP to the last-indexedpath. A per-project
.mcp.json(already produced bysave_root_mcp_config) would avoid the pollution.Both are worth a separate look; happy to file follow-ups if useful.
Severity
High — silent user-config destruction. No warning, no backup, no exit code.
Recoverable only if the user has shell history / a backup / version
control on their dotfiles.
Note from the reporter