Repository navigation
feat(mcp): move notes and folders into another project - #2048
Merged
Merged
Conversation
bm_move(destination_project=...) moves a note or a directory into another project. Per #1842, a cross-project move is a write in the target followed by a delete in the source: the note is new there (new id, its own history), and links are not rewritten. - The source is deleted only after the copy is written, and only while it is still the copied revision. A refused delete removes the copy again, so a refused move changes neither project. When the delete's outcome is unknown the copy stays, since the source may be gone. - A note already at the destination is never replaced; a locked note is refused before anything is written. - A directory moves note by note; files that are not notes stay and the result lists what did not move. - destination_project naming the source project is an ordinary move. - 403 answers now map to a FORBIDDEN refusal instead of OUTCOME_UNKNOWN. - bm tool move gets --destination-project; bm-move(3) documents it. Closes #1842 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMYVL4TcbWdXhdvAzxfqjo Signed-off-by: phernandez <paul@basicmachines.co>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
โน๏ธ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with ๐ while any review is running, comments if it has suggestions, and reacts with ๐ once all reviews finish with no findings. |
There was a problem hiding this comment.
๐ก Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9a15c1aa4e
โน๏ธ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with ๐.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The copy took its title from the destination file name, so a move that also renamed the file rewrote the title. The file name sets the path; the title comes from the source note. Update the integration test for the destination_project hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CMYVL4TcbWdXhdvAzxfqjo Signed-off-by: phernandez <paul@basicmachines.co>
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.
Closes #1842.
What it does
bm_move(..., destination_project="research")moves a note (identifier) or a directory (path) into another project.destinationis then a path in that project, and a trailing/keeps the name, as it does within a project.As the issue specifies, a cross-project move is a write in the target followed by a delete in the source:
ALREADY_EXISTS, nothing moved).destination_projectnaming the source's own project is an ordinary move, which keeps id and history.Safety
New module:
src/basic_memory/mcp/cross_project_move.py.Order of operations:
overwrite=false.If the note changes at any point, the guarded delete refuses.
When the source delete fails:
PARTIAL_FAILUREsays plainly that the note is now in both projects.Up-front refusals:
locked: true) is refused before anything is written. Its copy would be locked too and could not be removed.Directories move note by note. Files that aren't notes (attachments,
.txt) stay. A directory with anything left behind ends inPARTIAL_FAILURElisting each file that stayed.Write access: a caller who can't write the target, or delete in the source, is refused through the API's own answers. To support that,
refusals.pynow maps 403 to a newFORBIDDENcode instead ofOUTCOME_UNKNOWN. Before this, a 403 read as "may or may not have happened".Interface
destination_projectparameter onbm_move, and--destination-projectonbm tool move.destination_project, is still refused. The hint now points atdestination_project.Not included
Tests
tests/mcp/test_tool_bm_move_across_projects.py, 20 tests on the real ASGI stack. Covers:The failure cases use an httpx transport in front of the real app that answers 403 or drops the request for one HTTP method. No monkeypatching.
Coverage: 100% for
cross_project_move.pyandtools/move.py.tests/mcp+tests/clipass (2,524). The new tests also pass on Postgres.just fast-checkpasses.๐ค Generated with Claude Code
https://claude.ai/code/session_01CMYVL4TcbWdXhdvAzxfqjo