From 72c2d1687e1915c0add0faa6f4c4d0bd20be5dd5 Mon Sep 17 00:00:00 2001 From: agent_coder Date: Fri, 10 Jul 2026 00:06:28 +0300 Subject: [PATCH] =?UTF-8?q?docs(mcp):=20=D0=BF=D0=BE=D1=87=D0=B8=D0=BD?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D0=BE=D1=81=D0=B8=D1=80=D0=BE=D1=82=D0=B5?= =?UTF-8?q?=D0=B2=D1=88=D0=B8=D0=B9=20docstring=20+=20=D1=83=D1=82=D0=BE?= =?UTF-8?q?=D1=87=D0=BD=D0=B8=D1=82=D1=8C=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D1=82=20=D0=BF=D1=80=D0=BE=20exact-wins=20(=D1=80=D0=B5?= =?UTF-8?q?=D0=B2=D1=8C=D1=8E=20#427)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - text-normalize.ts: closestBlockHint был вставлен между docstring'ом stripInlineMarkdown и его определением -> docstring осиротел. closestBlockHint перенесён ПОСЛЕ stripInlineMarkdown, каждый docstring снова примыкает к своей функции. Поведение не менялось (только порядок объявлений). - comment-anchor.ts: header-коммент завышал маршрутизацию — countAnchorMatches НЕ зовёт resolveAnchorSelection, у него своя параллельная реализация exact-wins. Коммент уточнён: can/get/apply идут через resolveAnchorSelection, count держит свой счётчик-примитив, синхронный с ним; обе реализации exact-wins должны держаться в синхроне при правках. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/mcp/src/lib/comment-anchor.ts | 12 ++++--- packages/mcp/src/lib/text-normalize.ts | 46 +++++++++++++------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/packages/mcp/src/lib/comment-anchor.ts b/packages/mcp/src/lib/comment-anchor.ts index 5c94250b..4c16456d 100644 --- a/packages/mcp/src/lib/comment-anchor.ts +++ b/packages/mcp/src/lib/comment-anchor.ts @@ -22,10 +22,14 @@ * inline markdown (`**bold**`, `` `code` ``, `[t](u)`), the raw locator will not * match the document's plain text. Exactly like edit_page_text's json-edit * fallback, we first try the verbatim selection and, ONLY if it anchors nowhere - * in the whole document, retry with `stripInlineMarkdown` applied. This decision - * is made ONCE per public call in `resolveAnchorSelection`, so the four public - * entry points (can/count/get/apply) all agree on which locator matched — the - * suggestion-uniqueness gate depends on count and can/get never disagreeing. + * in the whole document, retry with `stripInlineMarkdown` applied. `canAnchorInDoc`, + * `getAnchoredText` and `applyAnchorInDoc` share this decision via + * `resolveAnchorSelection`. `countAnchorMatches` keeps its OWN parallel exact-wins + * implementation (it needs a raw match COUNT, not a single resolved locator), kept + * deliberately in sync with `resolveAnchorSelection`: raw match ⇒ use raw, else fall + * back to the stripped count. All four therefore agree on which locator matched — + * the suggestion-uniqueness gate depends on count and can/get never disagreeing, so + * these two exact-wins implementations MUST stay in sync if either is changed. */ import { stripInlineMarkdown } from "./text-normalize.js"; diff --git a/packages/mcp/src/lib/text-normalize.ts b/packages/mcp/src/lib/text-normalize.ts index cd545366..997cbff0 100644 --- a/packages/mcp/src/lib/text-normalize.ts +++ b/packages/mcp/src/lib/text-normalize.ts @@ -92,6 +92,29 @@ export function stripBalancedWrappers(s: string): string { * ORIGINAL string is returned so a locator can never normalize down to "" and * match everything. */ +export function stripInlineMarkdown(s: string): string { + if (typeof s !== "string" || s.length === 0) return s; + + // 1 + 2. Shared link/image and balanced-wrapper passes. + let out = stripWrappersAndLinks(s); + + // 3. Trim leading/trailing decoration: whitespace, leftover markdown markers, + // and emoji (Extended_Pictographic plus the VS16 / ZWJ joiners, plus the + // regional-indicator range U+1F1E6–U+1F1FF for flag emoji, which are NOT + // Extended_Pictographic). The `u` flag enables the Unicode property escape. + // Anchored runs only — interior text and sentence punctuation are untouched. + const DECORATION = + "[\\s*_~\\x60\\p{Extended_Pictographic}\\u{1F1E6}-\\u{1F1FF}\\u{FE0F}\\u{200D}]+"; + out = out + .replace(new RegExp("^" + DECORATION, "u"), "") + .replace(new RegExp(DECORATION + "$", "u"), ""); + + // 4. Never normalize a locator down to nothing. + if (out.length === 0) return s; + + return out; +} + /** * Build a bounded "closest text" hint for an anchor/find MISS, shared by * edit_page_text (json-edit) and create_comment (client) so both surface the @@ -125,26 +148,3 @@ export function closestBlockHint( points.length > 120 ? points.slice(0, 120).join("") + "…" : hitBlock; return ` Closest block text: "${snippet}".`; } - -export function stripInlineMarkdown(s: string): string { - if (typeof s !== "string" || s.length === 0) return s; - - // 1 + 2. Shared link/image and balanced-wrapper passes. - let out = stripWrappersAndLinks(s); - - // 3. Trim leading/trailing decoration: whitespace, leftover markdown markers, - // and emoji (Extended_Pictographic plus the VS16 / ZWJ joiners, plus the - // regional-indicator range U+1F1E6–U+1F1FF for flag emoji, which are NOT - // Extended_Pictographic). The `u` flag enables the Unicode property escape. - // Anchored runs only — interior text and sentence punctuation are untouched. - const DECORATION = - "[\\s*_~\\x60\\p{Extended_Pictographic}\\u{1F1E6}-\\u{1F1FF}\\u{FE0F}\\u{200D}]+"; - out = out - .replace(new RegExp("^" + DECORATION, "u"), "") - .replace(new RegExp(DECORATION + "$", "u"), ""); - - // 4. Never normalize a locator down to nothing. - if (out.length === 0) return s; - - return out; -}