klypix-mcp 1.20.0 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/klypix-format.mjs +22 -4
package/package.json
CHANGED
package/src/klypix-format.mjs
CHANGED
|
@@ -1334,7 +1334,11 @@ const POLARITY_RES = POLARITY_PAIRS.map(([x, y]) => ({ x, y, rx: new RegExp(`\\b
|
|
|
1334
1334
|
export function detectContradictions(struct, { minOverlap = 0.45, topK = 12 } = {}) {
|
|
1335
1335
|
if (!struct || !Array.isArray(struct.cards)) return [];
|
|
1336
1336
|
const isArchived = (c) => /^archive$/i.test(c.area || '');
|
|
1337
|
-
|
|
1337
|
+
// Legacy pre-v1.15 raw-bash ship cards are noise (path-scraped junk); they poison
|
|
1338
|
+
// reconcile with vocabulary-overlap false positives just as they did repeat-
|
|
1339
|
+
// matching, so exclude them here too (they're surfaced for cleanup by
|
|
1340
|
+
// findLegacyShipCards / brain_reconcile mode:legacy instead).
|
|
1341
|
+
const live = struct.cards.filter(c => c.type !== 'container' && (c.text || '').trim() && !isArchived(c) && !isLegacyRawShipCard(c.text));
|
|
1338
1342
|
// Cue meta words stripped up front — same-subject matching must compare the
|
|
1339
1343
|
// SUBJECT, not the vocabulary of the correction act (see stripCueMeta above).
|
|
1340
1344
|
const wset = new Map(live.map(c => [c.id, stripCueMeta(new Set(queryTokens(c.text)))]));
|
|
@@ -1635,15 +1639,27 @@ export function correctionOverlaysFor(struct, cards, { at = CORRECTION_SUPERSEDE
|
|
|
1635
1639
|
// caller annotates the card (never retires it — the fact is right, only its
|
|
1636
1640
|
// status decayed). Pure + cheap. PR numbers are capped at 5 digits — a 6+ digit
|
|
1637
1641
|
// "#N" is a path-scraped junk number (see isLegacyRawShipCard), never a real PR.
|
|
1638
|
-
const PR_MERGE_AWAIT_RE = /\b(?:await(?:s|ing)?|pending|not\s+yet|yet\s+to\s+be|to\s+be|unmerged|needs?)\b[^.\n]{0,24}?\bmerg/i;
|
|
1639
1642
|
const PR_MERGED_RE = /\bmerged\b[^.\n]{0,16}?(?:PR|pull\s+request|#)\s*#?(\d{1,5})\b/i;
|
|
1640
1643
|
const PR_REFS_RE = /\b(?:PR|pull\s+request|#)\s*#?(\d{1,5})\b/ig;
|
|
1644
|
+
// The awaits/pending-merge cue fragment, reused both directions below.
|
|
1645
|
+
const AWAIT_CUE = String.raw`(?:await(?:s|ing)?|pending|not\s+yet|yet\s+to\s+be|unmerged|needs?)\b[^.\n]{0,20}?merg`;
|
|
1641
1646
|
function prNumbersIn(text) {
|
|
1642
1647
|
const out = new Set(); const s = String(text || '');
|
|
1643
1648
|
PR_REFS_RE.lastIndex = 0; let m;
|
|
1644
1649
|
while ((m = PR_REFS_RE.exec(s))) out.add(m[1]);
|
|
1645
1650
|
return out;
|
|
1646
1651
|
}
|
|
1652
|
+
// PRECISION (field 2026-07-04): the awaits/pending cue must sit ADJACENT to THIS PR
|
|
1653
|
+
// ref — otherwise a long card that names "#850" only as an EXAMPLE and separately
|
|
1654
|
+
// says "awaits merge" (about something else) over-triggered the overlay. Require the
|
|
1655
|
+
// cue within ~30 chars of the specific "PR #N", in either order, same sentence.
|
|
1656
|
+
function awaitNearRef(text, num) {
|
|
1657
|
+
const n = String(num).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1658
|
+
const ref = String.raw`(?:PR|pull\s+request|#)\s*#?${n}\b`;
|
|
1659
|
+
const before = new RegExp(`${ref}[^.\\n]{0,30}?${AWAIT_CUE}`, 'i'); // "PR #7 awaits merge"
|
|
1660
|
+
const after = new RegExp(`${AWAIT_CUE}[^.\\n]{0,30}?${ref}`, 'i'); // "awaiting merge of PR #7"
|
|
1661
|
+
return before.test(text) || after.test(text);
|
|
1662
|
+
}
|
|
1647
1663
|
export function mergeOverlaysFor(struct, cards) {
|
|
1648
1664
|
const out = new Map();
|
|
1649
1665
|
if (!struct || !Array.isArray(struct.cards) || !Array.isArray(cards) || !cards.length) return out;
|
|
@@ -1660,10 +1676,12 @@ export function mergeOverlaysFor(struct, cards) {
|
|
|
1660
1676
|
for (const card of cards) {
|
|
1661
1677
|
if (!card || !card.id || !(card.text || '').trim()) continue;
|
|
1662
1678
|
if (PR_MERGED_RE.test(card.text)) continue; // the hit IS a merge note — nothing to overlay
|
|
1663
|
-
if (!PR_MERGE_AWAIT_RE.test(card.text)) continue; // must actually claim it's awaiting a merge
|
|
1664
1679
|
for (const num of prNumbersIn(card.text)) {
|
|
1665
1680
|
const by = mergedBy.get(num);
|
|
1666
|
-
if (by
|
|
1681
|
+
if (!by || by.id === card.id) continue;
|
|
1682
|
+
if (!awaitNearRef(card.text, num)) continue; // the awaits cue must be ADJACENT to THIS ref (no example-#N over-trigger)
|
|
1683
|
+
out.set(card.id, { kind: 'merged', num, by, date: by.createdAt ? new Date(by.createdAt).toISOString().slice(0, 10) : null });
|
|
1684
|
+
break;
|
|
1667
1685
|
}
|
|
1668
1686
|
}
|
|
1669
1687
|
return out;
|