Skip to content

fix(tools): make claude-memory file operations act on the exact file, literally#1285

Open
abhay-codes07 wants to merge 1 commit into
supermemoryai:mainfrom
abhay-codes07:fix/claude-memory-file-ops
Open

fix(tools): make claude-memory file operations act on the exact file, literally#1285
abhay-codes07 wants to merge 1 commit into
supermemoryai:mainfrom
abhay-codes07:fix/claude-memory-file-ops

Conversation

@abhay-codes07

Copy link
Copy Markdown
Contributor

What

Three related defects in the Claude memory tool's file operations (packages/tools/src/claude-memory.ts):

  1. str_replace corrupts content containing $ sequences. The model-authored replacement was passed to String.replace as a plain string, so JS replacement patterns were expanded: a new_str containing $&, $', $` or $$ silently wrote corrupted content while reporting success (replace TODO with price is $& now → stores price is TODO now). Memory files routinely contain literal dollar signs — code, prices, shell snippets.

  2. view could return the wrong file as a success. readFile searched with limit: 1 and then looked for the exact customId match inside that single result — while the sibling getFileDocument helper (used by every mutating command) uses limit: 5 precisely so the exact match is findable among semantic near-neighbours. If a similarly-named file ranked first (notes.txt vs notes_backup.txt), view returned the wrong file's contents with line numbers, as a success.

  3. Both lookups fell back to results[0] when no exact match existed — which let view read, and str_replace/insert/delete/rename modify, a different file than the one requested.

Fix

  • Function replacer keeps the replacement literal: originalContent.replace(oldStr, () => newStr)
  • readFile now goes through getFileDocument (removing the duplicated lookup and making view read the same raw || content the mutating commands edit)
  • The lookup requires the exact customId match and reports file-not-found otherwise — silently acting on a different file is strictly worse than an explicit error

Testing

  • New mocked-SDK tests: dollar-sequence literalness for all four patterns ($&, $', $`, $$), exact-match preference when a neighbour ranks first, and not-found (with no write issued) when only a different file comes back — six of the seven new tests fail against the previous implementation
  • vitest run src/claude-memory.test.ts — 10 pass
  • tsc --noEmit error count unchanged vs main (148 pre-existing), biome check clean on touched files

cc @MaheshtheDev

… literally

Three related defects in the Claude memory tool's file operations:

- str_replace passed the model-authored replacement to String.replace
  as a plain string, so JS replacement patterns were expanded: a
  new_str containing $&, $', $` or $$ silently wrote corrupted content
  while reporting success (e.g. replacing TODO with "price is $& now"
  stores "price is TODO now"). Memory files routinely contain literal
  dollar signs — code, prices, shell snippets. A function replacer
  keeps the value literal.

- readFile (the view command) searched with limit: 1 and then looked
  for the exact customId match inside that single result — the sibling
  getFileDocument helper uses limit: 5 precisely so the exact match is
  findable among semantic near-neighbours. If a similarly-named file
  ranked first, view returned the wrong file's contents as a success.
  readFile now goes through getFileDocument, which also removes the
  duplicated lookup and makes view read the same raw||content the
  mutating commands edit.

- both lookups fell back to results[0] when no exact customId match
  existed, which let view read — and str_replace/insert/delete/rename
  modify — a different file than the one requested. The lookup now
  requires the exact match and reports file-not-found otherwise.

Covered with mocked-SDK tests: dollar-sequence literalness for all four
patterns, exact-match preference when a neighbour ranks first, and
not-found (with no write issued) when only a different file comes back.
Six of the seven new tests fail against the previous implementation.
Copilot AI review requested due to automatic review settings July 13, 2026 12:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

2 participants