feat: add vault_replace_span and vault_insert_at_anchor tools - #495
Conversation
Complete the anchor-targeted edit triad alongside vault_delete_span. Both tools reuse the same anchor resolution (case-sensitive substring, ambiguity-is-error, first_match escape hatch) and share the same error semantics. vault_replace_span replaces a contiguous block of lines identified by start/end anchors with new content in one atomic write — replacing the fragile two-step delete_span + patch_note workaround. vault_insert_at_anchor inserts content before or after a line identified by an anchor — freeing edits from heading-only targeting. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
umm-actually re-reviewed at 1 new finding(s) posted (17 tracked finding(s) across all runs). Context notes
umm-actually · deepseek/deepseek-v4-flash-0731 |
…an and vault_insert_at_anchor descriptions Both tools accept user-authored content written as Obsidian Markdown but were missing the Obsidian syntax note that all other content-writing tools include (vault_write_note, vault_patch_note, vault_replace_in_note). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Add positive-plus-missing anchor test for insertAtAnchor
Tests at lines 3356-3383 cover only an ambiguous start anchor, ambiguous end anchor, and end anchor missing; the repository's own test code for the sibling deleteSpan has a 'positive-plus-missing' test that seeds a unique line from a non-anchor sentence and asserts the insert still succeeds because the phone line is absent. The insertAtAnchor error-path tests mirror the old deleteSpan question but drop that case, so a future breaking change to the resolver's sentence-boundary math would be caught, while the missing sibling case stays unprotected. Failure scenario: Inserting after an unambiguous list line whose sentence boundary spans lines, when the end anchor is an empty line and the resolver's state machine would mistake the boundary — no test asserts the intended result, so a regression would silently widen or narrow the insertion point. Suggested fixAdd a test that seeds a non-anchored line that would have caused an ambiguous-anchor rejection (like the existing end-anchor positive-plus-missing case) and asserts the insertion resolves past it without error.umm-actually · deepseek/deepseek-v4-flash-0731 |
- Make resolveAnchorLine role optional so vault_insert_at_anchor produces "anchor not found" / "ambiguous anchor" instead of the misleading "start anchor" qualifier (it has a single anchor param, not start/end) - Gate vault_delete_span's vault_replace_span reference via isToolEnabled so the tool name disappears when the tool is disabled - Update documented Errors prefix in vault_insert_at_anchor description Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…n/insertAtAnchor tests Two-bar fixes: "preserves frontmatter" tests now assert full content (previously passed as silent no-ops). Assertion quality: 6 substring/loose matchers replaced with exact assertions on deterministic output. Coverage gaps: add tests for ambiguous end anchor and firstMatch on end anchor in replaceSpan (deleteSpan had these; replaceSpan did not). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
vault_delete_span's description used isToolEnabled with manual ternaries to gate vault_replace_span references — the only tool in the file not using the whenToolEnabledText helper that every other cross-reference uses. Swapped to whenToolEnabledText and removed the unused isToolEnabled destructuring. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Verifies that content containing 3+ consecutive blank lines is preserved verbatim — the no-collapse invariant applies to internal content, not just seam-adjacent blanks. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
aliasunder
commented
Aug 26, 2026
Re: "Add positive-plus-missing anchor test for insertAtAnchor" — 🔍 ship-check · pr-monitor · Claude Opus 4.6 (1M context) |
Restore removed module-layering paragraph in AGENTS.md
The 5th paragraph of the Module layering section in AGENTS.md was removed by this PR, deleting the explanation that a module's folder is decided by its dependency profile, not its topic. This is unrelated to the two new tools and drops a core architectural convention from the project's documentation. Failure scenario: A developer reads AGENTS.md to determine where a new vault-operations module should live; the removed paragraph that established the dependency-profile rule (canvas.ts belongs in obsidian-markdown/ despite parsing JSON, pdf-engine.ts is the one side-effecting leaf) is gone, so the rationale for existing module placement is no longer documented. Suggested fixRestore the removed paragraph in the Module layering section, preserving the dependency-profile rule and the canvas.ts/pdf-engine.ts examples.umm-actually · deepseek/deepseek-v4-flash-0731 |
aliasunder
commented
Aug 26, 2026
Re: "Restore removed module-layering paragraph in AGENTS.md" — False positive. The PR's only change to AGENTS.md is a one-line update to the structure tree comment ( 🔍 ship-check · pr-monitor · Claude Opus 4.6 (1M context) |
aliasunder
commented
Aug 26, 2026
@CodeRabbit review |
✅ Action performedReview finished.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (10)
Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe PR adds ChangesAnchor-targeted Vault editing
Estimated code review effort: 4 (Complex) | ~45 minutes Merge Risk:⚪ Minimal · up to This additive change introduces two anchor-targeted editing tools without any supplied evidence of an actionable merge-blocking risk; no current issue remains beyond normal checks and review. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Full details: Docstring CoverageExplanation No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 6 files. (4 skipped: 4 unsupported.) ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Update vault-patcher module header to mention anchor operations
The module doc comment still says “Surgical note editing — heading-targeted patches and find-and-replace.” The module now also provides anchor-targeted replace ( Failure scenario: Reading the module header to locate edit operations does not name Suggested fixReword the header to include the anchor operations, e.g. “Surgical note editing — heading-targeted patches, find-and-replace, and anchor-targeted span replacement/insertion.”umm-actually · deepseek/deepseek-v4-flash-0731 |
# Conflicts: # README.md # src/__tests__/integration/server-integration.test.ts
aliasunder
commented
Aug 27, 2026
Re: "Update vault-patcher module header to mention anchor operations" — Fixed in 6e2a2d4. The header now reads "Surgical note editing — heading-targeted patches, find-and-replace, and anchor-targeted line spans (delete, replace, insert)", naming all three anchor operations the module owns. 🔍 ship-check · pr-monitor · claude-fable-5 |
…and return shape Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
…iptions Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…span ops; sync count comments and YAML caveat Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pin Obsidian-syntax guidance for the two new write tools
WRITE_TOOLS is a hardcoded list that feeds the it.each('%s description includes Obsidian syntax guidance') check, but it was not extended with vault_replace_span or vault_insert_at_anchor. Both tools are write tools whose descriptions do carry an 'Obsidian syntax:' section, so nothing fails today — but a future removal or rewording of that section in either new description is unpinned and would pass CI, while every other write tool is guarded. Failure scenario: A contributor edits vault_replace_span's description and drops the 'Obsidian syntax:' paragraph. The it.each(WRITE_TOOLS) test skips the tool because it is absent from the literal list, and the check suite passes despite the two new write tools losing documented Obsidian-syntax guidance. Suggested fixAdd TOOL_NAMES.VAULT_REPLACE_SPAN and TOOL_NAMES.VAULT_INSERT_AT_ANCHOR to the WRITE_TOOLS array so the new writers join the same Obsidian-syntax pin as vault_write_note and vault_patch_note.umm-actually · deepseek/deepseek-v4-flash-0731 |
…nsert_at_anchor Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
aliasunder
commented
Aug 27, 2026
Re: "Pin Obsidian-syntax guidance for the two new write tools" — Fixed in ae46147. 🔍 ship-check · pr-monitor · claude-fable-5 |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
vault_replace_span— replace a contiguous block of lines identified by short anchor substrings with new content, in one atomic write. Same anchor semantics asvault_delete_span(case-sensitive substring, ambiguity-is-error,first_matchescape hatch), withcollapseBlankRunson the reassembled body. Replaces the fragile two-stepdelete_span+patch_noteworkaround.vault_insert_at_anchor— insert content before or after a line identified by anchor, freeing edits from heading-only targeting. No blank-line collapse (insertion doesn't create gaps).vault_delete_span, every line-level edit (delete, replace, insert) can now be targeted by a short anchor substring instead of exact text, addressing ~82/month edit-side guess-miss errors (49replace_in_notetext-not-found + 33delete_spananchor-not-found from the Jul 9 – Aug 6 usage window).Design decisions
vault_replace_in_note(inline edits),vault_patch_note(heading-targeted), andvault_delete_span(anchor deletion) keep their distinct roles.vault_insert_at_anchorovervault_insert_span— descriptive accuracy over family-suffix consistency; "span" doesn't apply to an insert..min(1)) on both tools —vault_delete_spanexists for deletion.collapseBlankRunsonvault_replace_span(same asdelete_span) — prevents 3+ consecutive blank-line runs at the seam. Not applied onvault_insert_at_anchor(insertion doesn't create gaps).vault_insert_at_anchoris annotated as an additive write (destructiveHint: false) — it can only add lines, never overwrite or remove them, matchingvault_create_task.vault_replace_spanstays destructive. The sharedADDITIVE_WRITE_ANNOTATIONSconstant now backs both additive tools.resolveSpanLinesresolves{ startLine, endLine }fordeleteSpanandreplaceSpan; all three ops splice withArray.prototype.toSpliced.whenToolEnabledText, so a reference disappears when its target is disabled.content(adds a blank line), verbatim insert vs. collapsed replace, what eachpositionvalue does, and the literal confirmation-message shape each tool returns.Changes
vault-patcher.tsreplaceSpan+insertAtAnchor; sharedresolveSpanLines;toSplicedin all span opstool-registry.tsTOOL_NAMES+TOOL_REGISTRYentries;ADDITIVE_WRITE_ANNOTATIONSvault-crud-tools.tsvault_patch_notestates its no-separatorcontentsemantics and literal return shapevault-patcher.test.tstool-registry.test.tsserver-integration.test.tsserver-error-contracts.test.tsREADME.mdARCHITECTURE.mdAGENTS.mdDOCKERHUB.mdTest plan
npm run buildclean (server + CLI)npm run lint— 0 errorstools/listJSON Schema inspected from a booted server for both tools —requiredsets, stringenumforposition,minLengthfloors, parameter descriptions, and annotations all as intended:remotebuild of this branch (6e2a2d4; the two later commits are docs-only), driven through an MCP client against a real vault — 26 calls to the two new tools on a throwaway note, every result read back and compared against the documented contract:vault_replace_spanwrites: single-line table row; multi-line callout withend_anchor;start_anchorandend_anchoron the same line; trailing newline incontentadds one blank line and the seam collapses to a single blank; leading/trailing blank padding incontentcollapses;first_matchtakes the first of two matches; frontmatter preserved on every write; a property value matching the anchor text is not a match (body only).vault_replace_spanerror paths (exact messages): start anchor not found; ambiguous start anchor (2 lines); ambiguous end anchor at or after the start; end anchor only above the start → "end anchor not found … at or after the start anchor"; note not found; hidden path blocked; anchor present only in frontmatter → not found; path without.md.vault_insert_at_anchorwrites:aftera table row;beforea heading with a trailing newline (callout + blank line);beforewithfirst_matchon a duplicated list item;afterthe last line with a multi-line block whose internal blank line is kept verbatim and no collapse at the seam.vault_insert_at_anchorerror paths: anchor not found; ambiguous anchor; note not found; hidden path blocked; anchor present only in frontmatter → not found; path without.md; invalidpositionrejected at the schema layer (-32602).vault_delete_span,vault_replace_in_note,vault_patch_note,vault_create_task,vault_update_task,vault_read_note(full + heading), plusvault_search,vault_list_tasks,vault_get_backlinks,vault_get_daily_note,vault_memory_recall,vault_list_files— all behaved as before.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation
Tests