Skip to content

feat(install): disable Claude Code built-in memory during setup#155

Merged
wcatz merged 3 commits intomainfrom
feat/disable-claude-builtin-memory
Apr 25, 2026
Merged

feat(install): disable Claude Code built-in memory during setup#155
wcatz merged 3 commits intomainfrom
feat/disable-claude-builtin-memory

Conversation

@wcatz
Copy link
Copy Markdown
Owner

@wcatz wcatz commented Apr 25, 2026

Problem

Claude Code maintains its own flat-file memory system under ~/.claude/projects/*/memory/. When Ghost is installed alongside it, both systems compete:

  • Claude Code keeps writing its own memories (via autoMemoryEnabled, which defaults to true)
  • At session start, the file-memory injects stale or conflicting context before Ghost's SessionStart hook fires
  • Claude sees the file-memory answer first and may skip querying Ghost's richer store
  • Users end up with two diverging memory systems that contradict each other

This was discovered in a real session where stale file-memory was overriding Ghost context and causing incorrect behavior.

Fix

ghost mcp init now includes a new step 5/7 that sets "autoMemoryEnabled": false in ~/.claude/settings.json, stopping Claude Code from writing to its own file-memory during sessions where Ghost is active.

What changes

  • settings.go: setAutoMemoryEnabled(v bool) / getAutoMemoryEnabled() — idempotent read-modify-write on settingsFile; merges with all existing keys, never clobbers
  • init.go: ensureAutoMemoryDisabled() added as step 5/7; dry-run aware; rides the same atomic save used by the permissions and hook steps
  • status.go: ghost mcp status now surfaces a check for autoMemoryEnabled: false
  • README.md: step table updated (6→7), new step documented

Properties

  • Idempotent — re-running ghost mcp init when the flag is already false is a no-op
  • Non-destructive — only the autoMemoryEnabled key is touched; all other settings are preserved
  • Atomic — the flag is written in the same temp-file + rename operation as permissions and hooks
  • Safe to --dry-run — reports what would change without writing anything

Tests added

  • 7 new settings_test.go cases: absent key, idempotent (false→false), override (true→false), key-preservation, round-trip, getAutoMemoryEnabled absent
  • 3 new init_test.go cases: sets flag, idempotent, dry-run

Out of scope

Aggressively cleaning up existing ~/.claude/projects/*/memory/ directories is intentionally deferred. Ghost's step 7 continues to write MEMORY.md redirects into those dirs to guide Claude toward Ghost; removing the dirs would break that mechanism.

Test plan

  • go test ./internal/mcpinit/... — all 38 tests pass
  • go test ./... — full suite green
  • go vet ./... — clean
  • Fresh install: ghost mcp init shows [5/7] Disabling Claude Code built-in memory... + set autoMemoryEnabled: false
  • Re-run: shows ✓ autoMemoryEnabled already false
  • ghost mcp status reports the new check

Summary by CodeRabbit

  • New Features

    • Initialization now includes an explicit step to disable Claude Code's built-in flat-file memory.
  • Documentation

    • README walkthrough updated to show the new setup step and adjusted progress counters and summary table.
  • Bug Fixes / Health Checks

    • Status/verifier now validates that built-in memory is disabled and reports an unhealthy status with guidance if not.

Claude Code maintains its own flat-file memory system
(~/.claude/projects/*/memory/) that competes with Ghost: both systems
write memories, the file-memory injects stale/conflicting context at
session start, and Claude consults it before Ghost's richer store because
it appears to have already loaded an answer.

Fix: ghost mcp init now sets "autoMemoryEnabled": false in
~/.claude/settings.json (step 5/7) to stop Claude Code writing to its
own file-memory during sessions where Ghost is active.

- settings.go: add setAutoMemoryEnabled / getAutoMemoryEnabled on
  settingsFile; idempotent, merges with existing settings, never clobbers
  other keys
- init.go: add ensureAutoMemoryDisabled() as step 5; dry-run aware;
  rides the same atomic save as permissions/hook steps
- status.go: surface autoMemoryEnabled check in ghost mcp status
- README.md: update step table (6→7 steps) and document the new step
- settings_test.go: 7 new tests covering absent, idempotent, override,
  key-preservation, and round-trip cases
- init_test.go: 3 new tests covering set, idempotent, and dry-run cases

Signed-off-by: wcatz <wayne@blinklabs.io>
Signed-off-by: wcatz <waynecataldo@gmail.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 25, 2026

Warning

Rate limit exceeded

@wcatz has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 56 minutes and 34 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 56 minutes and 34 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 24f8667d-0563-4c8a-ad6c-bcec5c5ad8a4

📥 Commits

Reviewing files that changed from the base of the PR and between 59e5075 and 5240d29.

📒 Files selected for processing (1)
  • internal/mcpinit/init.go
📝 Walkthrough

Walkthrough

The PR adds an explicit step to disable Claude Code's built-in flat-file memory by writing autoMemoryEnabled: false to ~/.claude/settings.json, integrates an idempotent helper to perform that update (with dry-run reporting), updates init/status flows to account for the new step, and adds tests and README documentation reflecting a 7-step init flow.

Changes

Cohort / File(s) Summary
Documentation
README.md
Documented new "Disable built-in memory" step in the ghost mcp init walkthrough; example output counters updated from 6→7 steps and setup table added row.
Core init flow & tests
internal/mcpinit/init.go, internal/mcpinit/init_test.go
Inserted new step in Run() to call ensureAutoMemoryDisabled; renumbered subsequent steps; added unit tests covering dry-run and live behaviors.
Settings management & tests
internal/mcpinit/settings.go, internal/mcpinit/settings_test.go
Added unexported setAutoMemoryEnabled and getAutoMemoryEnabled on settingsFile for idempotent writes and presence-aware reads; tests for creation, idempotency, override, preservation, and persistence.
Status check
internal/mcpinit/status.go
Extended status verification to read autoMemoryEnabled and fail the check if the setting is present and not explicitly false.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hop to the settings, soft and light,
Flipping a flag in the quiet night.
From six to seven, the steps align,
Memory tucked away, set to fine,
Init complete — a carrot for delight! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: disabling Claude Code's built-in memory during Ghost's setup process. It directly reflects the primary objective across all modified files (init.go, settings.go, status.go, tests, and README).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/disable-claude-builtin-memory

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
internal/mcpinit/status.go (1)

88-88: ⚠️ Potential issue | 🟡 Minor

Stale step number in comment.

With the added autoMemoryEnabled check renumbering settings checks to 4-6, the project-redirects comment should be // 7. instead of // 6..

📝 Suggested fix
-	// 6. Project redirects.
+	// 7. Project redirects.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/mcpinit/status.go` at line 88, Update the stale comment numbering
for the project-redirects step in internal/mcpinit/status.go: change the comment
text "// 6. Project redirects." to "// 7. Project redirects." to reflect the
inserted autoMemoryEnabled check that pushed the settings checks to 4-6; locate
the comment near the settings validation block where steps are enumerated and
adjust only the numeric label.
internal/mcpinit/init.go (1)

27-75: ⚠️ Potential issue | 🔴 Critical

CI blocker: errcheck failing on modified fmt.Fprintln calls.

golangci-lint is failing on the renumbered banner lines ([1/7], [2/7], [3/7] at lines 27, 34, 40, and likely the rest of the [N/7] lines that were touched). Even though these are just text-renumber edits, modifying the lines re-triggers the errcheck rule.

Quickest fix is to discard the return explicitly on each touched call, e.g.:

📝 Suggested fix pattern
-	fmt.Fprintln(w, "[1/7] Checking prerequisites...")
+	_, _ = fmt.Fprintln(w, "[1/7] Checking prerequisites...")

Apply to lines 27, 34, 40, 47, 53, 68, 75 (and any other touched fmt.Fprintln/fmt.Fprintf calls flagged by CI).

Alternatively, add a small local helper or relax the errcheck config — but the pipeline must be green before merge.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/mcpinit/init.go` around lines 27 - 75, The failing errcheck is due
to unchecked returns from the fmt.Fprintln/Fprintf calls in init.go around the
MCP init flow; update each touched call (the banners and the import error print
near the functions checkPrereqs, registerMCP, ensurePermissions, ensureHook,
ensureAutoMemoryDisabled, importMemories and the settings write block) to
explicitly discard the returned values (e.g., assign to the blank identifier) so
the error is considered handled; apply this to all modified fmt.Fprintln/Fprintf
invocations in that snippet so CI passes.
🧹 Nitpick comments (1)
internal/mcpinit/init_test.go (1)

160-253: Good coverage of the three branches (set, idempotent, dry-run).

Tests verify both output messages and in-memory state, including the important dry-run invariant that settings are not mutated.

One small gap: no test exercises the autoMemoryEnabled: true → would-overwrite path in dry-run mode (i.e., dry-run should still emit would set ... false when the key currently holds true). Optional to add.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/mcpinit/init_test.go` around lines 160 - 253, Add a new unit test
that covers the dry-run branch when the current setting is true: create a temp
HOME and settings.json containing {"autoMemoryEnabled":true}, call loadSettings
and then ensureAutoMemoryDisabled with dryRun=true, assert the output contains
"would set autoMemoryEnabled: false", and assert the in-memory setting
(sf.getAutoMemoryEnabled()) remains true (i.e., dry run does not mutate state).
Use the existing test patterns in TestEnsureAutoMemoryDisabled_DryRun to locate
usage of loadSettings, ensureAutoMemoryDisabled, and sf.getAutoMemoryEnabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@internal/mcpinit/init.go`:
- Around line 27-75: The failing errcheck is due to unchecked returns from the
fmt.Fprintln/Fprintf calls in init.go around the MCP init flow; update each
touched call (the banners and the import error print near the functions
checkPrereqs, registerMCP, ensurePermissions, ensureHook,
ensureAutoMemoryDisabled, importMemories and the settings write block) to
explicitly discard the returned values (e.g., assign to the blank identifier) so
the error is considered handled; apply this to all modified fmt.Fprintln/Fprintf
invocations in that snippet so CI passes.

In `@internal/mcpinit/status.go`:
- Line 88: Update the stale comment numbering for the project-redirects step in
internal/mcpinit/status.go: change the comment text "// 6. Project redirects."
to "// 7. Project redirects." to reflect the inserted autoMemoryEnabled check
that pushed the settings checks to 4-6; locate the comment near the settings
validation block where steps are enumerated and adjust only the numeric label.

---

Nitpick comments:
In `@internal/mcpinit/init_test.go`:
- Around line 160-253: Add a new unit test that covers the dry-run branch when
the current setting is true: create a temp HOME and settings.json containing
{"autoMemoryEnabled":true}, call loadSettings and then ensureAutoMemoryDisabled
with dryRun=true, assert the output contains "would set autoMemoryEnabled:
false", and assert the in-memory setting (sf.getAutoMemoryEnabled()) remains
true (i.e., dry run does not mutate state). Use the existing test patterns in
TestEnsureAutoMemoryDisabled_DryRun to locate usage of loadSettings,
ensureAutoMemoryDisabled, and sf.getAutoMemoryEnabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16758301-f3f6-42e6-862c-457b8b283ed7

📥 Commits

Reviewing files that changed from the base of the PR and between 60803da and 8f805da.

📒 Files selected for processing (6)
  • README.md
  • internal/mcpinit/init.go
  • internal/mcpinit/init_test.go
  • internal/mcpinit/settings.go
  • internal/mcpinit/settings_test.go
  • internal/mcpinit/status.go

Signed-off-by: wcatz <waynecataldo@gmail.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/mcpinit/init.go (1)

47-75: ⚠️ Potential issue | 🔴 Critical

Fix CI: unchecked fmt.Fprintln errors are failing golangci-lint (errcheck).

The CI pipeline fails on lines 47, 53, and 68 because these fmt.Fprintln calls don't discard their return values, while lines 27/34/40 in this same function use the _, _ = form. The helper at lines 232/234/244/246 has the same inconsistency. Either prefix with _, _ = (matching the pattern already established in this file) or use fmt.Fprintf(w, "...\n", ...) consistently.

🛠 Proposed fix to silence errcheck on the new/touched lines
-	fmt.Fprintln(w, "\n[4/7] Configuring SessionStart hook...")
+	_, _ = fmt.Fprintln(w, "\n[4/7] Configuring SessionStart hook...")
@@
-	// Step 5: Disable Claude Code's built-in file memory.
-	fmt.Fprintln(w, "\n[5/7] Disabling Claude Code built-in memory...")
+	// Step 5: Disable Claude Code's built-in file memory.
+	_, _ = fmt.Fprintln(w, "\n[5/7] Disabling Claude Code built-in memory...")
@@
-	// Step 6: Import Claude Code memories.
-	fmt.Fprintln(w, "\n[6/7] Importing Claude Code memories...")
+	// Step 6: Import Claude Code memories.
+	_, _ = fmt.Fprintln(w, "\n[6/7] Importing Claude Code memories...")
@@
-	// Step 7: Project memory redirects.
-	fmt.Fprintln(w, "\n[7/7] Writing project memory redirects...")
+	// Step 7: Project memory redirects.
+	_, _ = fmt.Fprintln(w, "\n[7/7] Writing project memory redirects...")

And in ensureAutoMemoryDisabled:

-			fmt.Fprintln(w, "  ~ would set autoMemoryEnabled: false")
+			_, _ = fmt.Fprintln(w, "  ~ would set autoMemoryEnabled: false")
 		} else {
-			fmt.Fprintln(w, "  ✓ autoMemoryEnabled already false")
+			_, _ = fmt.Fprintln(w, "  ✓ autoMemoryEnabled already false")
 		}
@@
-		fmt.Fprintln(w, "  + set autoMemoryEnabled: false (disables competing file-memory)")
+		_, _ = fmt.Fprintln(w, "  + set autoMemoryEnabled: false (disables competing file-memory)")
 	} else {
-		fmt.Fprintln(w, "  ✓ autoMemoryEnabled already false")
+		_, _ = fmt.Fprintln(w, "  ✓ autoMemoryEnabled already false")
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/mcpinit/init.go` around lines 47 - 75, Several fmt.Fprintln calls in
init.go are ignoring their returned (int, error) values which fails errcheck;
update the calls at the SessionStart/configure, disabling memory, and importing
memories sections (the fmt.Fprintln calls around the calls to ensureHook,
ensureAutoMemoryDisabled, and before importMemories) to explicitly discard
returns using the existing file pattern (e.g., "_, _ = fmt.Fprintln(...)"). Also
apply the same change to the helper fmt.Fprintln calls referenced near the
helpers (the lines around ensureAutoMemoryDisabled and related helpers) so all
fmt.Fprintln usage in this file consistently uses "_, _ =" to silence errcheck.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@internal/mcpinit/init.go`:
- Around line 47-75: Several fmt.Fprintln calls in init.go are ignoring their
returned (int, error) values which fails errcheck; update the calls at the
SessionStart/configure, disabling memory, and importing memories sections (the
fmt.Fprintln calls around the calls to ensureHook, ensureAutoMemoryDisabled, and
before importMemories) to explicitly discard returns using the existing file
pattern (e.g., "_, _ = fmt.Fprintln(...)"). Also apply the same change to the
helper fmt.Fprintln calls referenced near the helpers (the lines around
ensureAutoMemoryDisabled and related helpers) so all fmt.Fprintln usage in this
file consistently uses "_, _ =" to silence errcheck.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fc0fea56-e87e-49b4-b79d-adeaf85a5b4c

📥 Commits

Reviewing files that changed from the base of the PR and between 8f805da and 59e5075.

📒 Files selected for processing (1)
  • internal/mcpinit/init.go

Signed-off-by: wcatz <waynecataldo@gmail.com>
@wcatz wcatz merged commit 1bb202a into main Apr 25, 2026
5 checks passed
@wcatz wcatz deleted the feat/disable-claude-builtin-memory branch April 25, 2026 20:12
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.

1 participant