@mrsf/cli 0.5.0 → 0.6.0
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/dist/browser.cjs +177 -18
- package/dist/browser.cjs.map +4 -4
- package/dist/browser.d.ts +7 -1
- package/dist/browser.js +6 -0
- package/dist/commands/reanchor.js +3 -0
- package/dist/index.cjs +106 -59
- package/dist/index.cjs.map +4 -4
- package/dist/lib/parser.d.ts +4 -19
- package/dist/lib/parser.js +4 -140
- package/dist/lib/reanchor-core.d.ts +13 -0
- package/dist/lib/reanchor-core.js +79 -18
- package/dist/lib/reanchor.js +4 -2
- package/dist/lib/serialize.d.ts +37 -0
- package/dist/lib/serialize.js +170 -0
- package/dist/lib/types.d.ts +7 -0
- package/dist/lib/writer.d.ts +1 -8
- package/dist/lib/writer.js +5 -14
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -31,20 +31,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
));
|
|
32
32
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
33
|
|
|
34
|
-
// src/lib/
|
|
35
|
-
var parser_exports = {};
|
|
36
|
-
__export(parser_exports, {
|
|
37
|
-
parseSidecar: () => parseSidecar,
|
|
38
|
-
parseSidecarContent: () => parseSidecarContent,
|
|
39
|
-
parseSidecarContentLenient: () => parseSidecarContentLenient,
|
|
40
|
-
parseSidecarLenient: () => parseSidecarLenient,
|
|
41
|
-
readDocumentLines: () => readDocumentLines
|
|
42
|
-
});
|
|
43
|
-
async function parseSidecar(filePath) {
|
|
44
|
-
const abs = import_node_path3.default.resolve(filePath);
|
|
45
|
-
const content = await (0, import_promises2.readFile)(abs, "utf-8");
|
|
46
|
-
return parseSidecarContent(content, abs);
|
|
47
|
-
}
|
|
34
|
+
// src/lib/serialize.ts
|
|
48
35
|
function parseSidecarContent(content, filenameHint) {
|
|
49
36
|
const trimmed = content.trim();
|
|
50
37
|
let parsed;
|
|
@@ -67,16 +54,6 @@ function parseSidecarContent(content, filenameHint) {
|
|
|
67
54
|
}
|
|
68
55
|
return parsed;
|
|
69
56
|
}
|
|
70
|
-
async function parseSidecarLenient(filePath) {
|
|
71
|
-
const abs = import_node_path3.default.resolve(filePath);
|
|
72
|
-
let content;
|
|
73
|
-
try {
|
|
74
|
-
content = await (0, import_promises2.readFile)(abs, "utf-8");
|
|
75
|
-
} catch (e) {
|
|
76
|
-
return { doc: null, error: `Cannot read file: ${e.message}` };
|
|
77
|
-
}
|
|
78
|
-
return parseSidecarContentLenient(content, abs);
|
|
79
|
-
}
|
|
80
57
|
function parseSidecarContentLenient(content, filenameHint) {
|
|
81
58
|
const trimmed = content.trim();
|
|
82
59
|
if (!trimmed) {
|
|
@@ -163,18 +140,59 @@ function salvageYaml(content) {
|
|
|
163
140
|
partialComments: salvaged.length > 0 ? salvaged : void 0
|
|
164
141
|
};
|
|
165
142
|
}
|
|
143
|
+
function toYaml(doc) {
|
|
144
|
+
const yamlDoc = new import_yaml.Document(doc);
|
|
145
|
+
return yamlDoc.toString({ lineWidth: 0 });
|
|
146
|
+
}
|
|
147
|
+
function toJson(doc) {
|
|
148
|
+
return JSON.stringify(doc, null, 2) + "\n";
|
|
149
|
+
}
|
|
150
|
+
var import_js_yaml2, import_yaml;
|
|
151
|
+
var init_serialize = __esm({
|
|
152
|
+
"src/lib/serialize.ts"() {
|
|
153
|
+
"use strict";
|
|
154
|
+
import_js_yaml2 = __toESM(require("js-yaml"), 1);
|
|
155
|
+
import_yaml = require("yaml");
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// src/lib/parser.ts
|
|
160
|
+
var parser_exports = {};
|
|
161
|
+
__export(parser_exports, {
|
|
162
|
+
parseSidecar: () => parseSidecar,
|
|
163
|
+
parseSidecarContent: () => parseSidecarContent,
|
|
164
|
+
parseSidecarContentLenient: () => parseSidecarContentLenient,
|
|
165
|
+
parseSidecarLenient: () => parseSidecarLenient,
|
|
166
|
+
readDocumentLines: () => readDocumentLines
|
|
167
|
+
});
|
|
168
|
+
async function parseSidecar(filePath) {
|
|
169
|
+
const abs = import_node_path3.default.resolve(filePath);
|
|
170
|
+
const content = await (0, import_promises2.readFile)(abs, "utf-8");
|
|
171
|
+
return parseSidecarContent(content, abs);
|
|
172
|
+
}
|
|
173
|
+
async function parseSidecarLenient(filePath) {
|
|
174
|
+
const abs = import_node_path3.default.resolve(filePath);
|
|
175
|
+
let content;
|
|
176
|
+
try {
|
|
177
|
+
content = await (0, import_promises2.readFile)(abs, "utf-8");
|
|
178
|
+
} catch (e) {
|
|
179
|
+
return { doc: null, error: `Cannot read file: ${e.message}` };
|
|
180
|
+
}
|
|
181
|
+
return parseSidecarContentLenient(content, abs);
|
|
182
|
+
}
|
|
166
183
|
async function readDocumentLines(filePath) {
|
|
167
184
|
const content = await (0, import_promises2.readFile)(import_node_path3.default.resolve(filePath), "utf-8");
|
|
168
185
|
const lines = content.replace(/\r\n?/g, "\n").split("\n");
|
|
169
186
|
return ["", ...lines];
|
|
170
187
|
}
|
|
171
|
-
var import_promises2, import_node_path3
|
|
188
|
+
var import_promises2, import_node_path3;
|
|
172
189
|
var init_parser = __esm({
|
|
173
190
|
"src/lib/parser.ts"() {
|
|
174
191
|
"use strict";
|
|
175
192
|
import_promises2 = require("node:fs/promises");
|
|
176
193
|
import_node_path3 = __toESM(require("node:path"), 1);
|
|
177
|
-
|
|
194
|
+
init_serialize();
|
|
195
|
+
init_serialize();
|
|
178
196
|
}
|
|
179
197
|
});
|
|
180
198
|
|
|
@@ -358,7 +376,9 @@ var import_promises3 = require("node:fs/promises");
|
|
|
358
376
|
var import_node_fs2 = require("node:fs");
|
|
359
377
|
var import_node_crypto = require("node:crypto");
|
|
360
378
|
var import_node_path4 = __toESM(require("node:path"), 1);
|
|
361
|
-
var
|
|
379
|
+
var import_yaml2 = require("yaml");
|
|
380
|
+
init_serialize();
|
|
381
|
+
init_serialize();
|
|
362
382
|
var writeQueue = /* @__PURE__ */ new Map();
|
|
363
383
|
function enqueueWrite(abs, fn) {
|
|
364
384
|
const prev = writeQueue.get(abs) ?? Promise.resolve();
|
|
@@ -558,13 +578,6 @@ function deepEqual(a, b) {
|
|
|
558
578
|
if (typeof a === "number" && typeof b === "number") return a === b;
|
|
559
579
|
return false;
|
|
560
580
|
}
|
|
561
|
-
function toYaml(doc) {
|
|
562
|
-
const yamlDoc = new import_yaml.Document(doc);
|
|
563
|
-
return yamlDoc.toString({ lineWidth: 0 });
|
|
564
|
-
}
|
|
565
|
-
function toJson(doc) {
|
|
566
|
-
return JSON.stringify(doc, null, 2) + "\n";
|
|
567
|
-
}
|
|
568
581
|
async function writeSidecar(filePath, doc) {
|
|
569
582
|
const abs = import_node_path4.default.resolve(filePath);
|
|
570
583
|
return enqueueWrite(abs, () => writeSidecarInternal(abs, doc));
|
|
@@ -593,14 +606,14 @@ async function writeSidecarInternal(abs, doc) {
|
|
|
593
606
|
}
|
|
594
607
|
let tokens;
|
|
595
608
|
try {
|
|
596
|
-
tokens = [...new
|
|
609
|
+
tokens = [...new import_yaml2.Parser().parse(raw)];
|
|
597
610
|
} catch {
|
|
598
611
|
await atomicWriteFile(abs, toYaml(doc));
|
|
599
612
|
return;
|
|
600
613
|
}
|
|
601
614
|
let currentDoc = null;
|
|
602
615
|
try {
|
|
603
|
-
currentDoc = (0,
|
|
616
|
+
currentDoc = (0, import_yaml2.parse)(raw);
|
|
604
617
|
} catch {
|
|
605
618
|
}
|
|
606
619
|
if (currentDoc && !Array.isArray(currentDoc.comments)) {
|
|
@@ -687,7 +700,7 @@ async function writeSidecarInternal(abs, doc) {
|
|
|
687
700
|
}
|
|
688
701
|
}
|
|
689
702
|
}
|
|
690
|
-
const result = tokens.map((t) =>
|
|
703
|
+
const result = tokens.map((t) => import_yaml2.CST.stringify(t)).join("");
|
|
691
704
|
await atomicWriteFile(abs, result);
|
|
692
705
|
}
|
|
693
706
|
|
|
@@ -1349,11 +1362,13 @@ async function detectRenames(fromCommit, toCommit, repoRoot) {
|
|
|
1349
1362
|
// src/lib/reanchor-core.ts
|
|
1350
1363
|
var HIGH_THRESHOLD = 0.8;
|
|
1351
1364
|
var DEFAULT_THRESHOLD = 0.6;
|
|
1365
|
+
var DEFAULT_PROXIMITY_WINDOW = 5;
|
|
1352
1366
|
function toReanchorLines(documentText) {
|
|
1353
1367
|
return ["", ...documentText.replace(/\r\n/g, "\n").split("\n")];
|
|
1354
1368
|
}
|
|
1355
1369
|
function reanchorComment(comment, documentLines, opts = {}) {
|
|
1356
1370
|
const threshold = opts.threshold ?? DEFAULT_THRESHOLD;
|
|
1371
|
+
const proximityWindow = opts.proximityWindow ?? DEFAULT_PROXIMITY_WINDOW;
|
|
1357
1372
|
const commentId = comment.id;
|
|
1358
1373
|
const selectedText = comment.selected_text;
|
|
1359
1374
|
if (!selectedText && comment.line == null) {
|
|
@@ -1404,30 +1419,46 @@ function reanchorComment(comment, documentLines, opts = {}) {
|
|
|
1404
1419
|
}
|
|
1405
1420
|
if (selectedText) {
|
|
1406
1421
|
const exactCandidates = exactMatch(documentLines, selectedText);
|
|
1422
|
+
let chosen;
|
|
1423
|
+
let chosenReason = "";
|
|
1407
1424
|
if (exactCandidates.length === 1) {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1425
|
+
chosen = exactCandidates[0];
|
|
1426
|
+
chosenReason = "Exact text match (unique).";
|
|
1427
|
+
} else if (exactCandidates.length > 1 && comment.line != null) {
|
|
1428
|
+
chosen = closestToLine(exactCandidates, comment.line);
|
|
1429
|
+
chosenReason = `Exact text match (${exactCandidates.length} occurrences; chose nearest to original line ${comment.line}).`;
|
|
1430
|
+
}
|
|
1431
|
+
if (chosen) {
|
|
1432
|
+
if (isImplausibleExactRelocation(comment, chosen, documentLines, proximityWindow)) {
|
|
1433
|
+
const textAtOrigin = extractText(
|
|
1434
|
+
documentLines,
|
|
1435
|
+
comment.line,
|
|
1436
|
+
comment.end_line,
|
|
1437
|
+
comment.start_column,
|
|
1438
|
+
comment.end_column
|
|
1439
|
+
);
|
|
1440
|
+
return {
|
|
1441
|
+
commentId,
|
|
1442
|
+
status: "fuzzy",
|
|
1443
|
+
score: 0.5,
|
|
1444
|
+
newLine: comment.line,
|
|
1445
|
+
newEndLine: comment.end_line,
|
|
1446
|
+
newStartColumn: comment.start_column,
|
|
1447
|
+
newEndColumn: comment.end_column,
|
|
1448
|
+
anchoredText: textAtOrigin ?? void 0,
|
|
1449
|
+
previousSelectedText: selectedText,
|
|
1450
|
+
reason: `Lone exact match at line ${chosen.line} is beyond the proximity window (\xB1${proximityWindow}) of original line ${comment.line} and the text at the original position changed; kept at original position, needs re-anchoring.`
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1422
1453
|
return {
|
|
1423
1454
|
commentId,
|
|
1424
1455
|
status: "anchored",
|
|
1425
1456
|
score: 1,
|
|
1426
|
-
newLine:
|
|
1427
|
-
newEndLine:
|
|
1428
|
-
newStartColumn:
|
|
1429
|
-
newEndColumn:
|
|
1430
|
-
reason:
|
|
1457
|
+
newLine: chosen.line,
|
|
1458
|
+
newEndLine: chosen.endLine,
|
|
1459
|
+
newStartColumn: chosen.startColumn,
|
|
1460
|
+
newEndColumn: chosen.endColumn,
|
|
1461
|
+
reason: chosenReason
|
|
1431
1462
|
};
|
|
1432
1463
|
}
|
|
1433
1464
|
const normCandidates = normalizedMatch(documentLines, selectedText);
|
|
@@ -1685,6 +1716,20 @@ function closestToLine(candidates, targetLine) {
|
|
|
1685
1716
|
(best, candidate) => Math.abs(candidate.line - targetLine) < Math.abs(best.line - targetLine) ? candidate : best
|
|
1686
1717
|
);
|
|
1687
1718
|
}
|
|
1719
|
+
function isImplausibleExactRelocation(comment, candidate, lines, proximityWindow) {
|
|
1720
|
+
if (comment.line == null) return false;
|
|
1721
|
+
if (comment.line <= 0 || comment.line >= lines.length) return false;
|
|
1722
|
+
if (Math.abs(candidate.line - comment.line) <= proximityWindow) return false;
|
|
1723
|
+
const textAtOrigin = extractText(
|
|
1724
|
+
lines,
|
|
1725
|
+
comment.line,
|
|
1726
|
+
comment.end_line,
|
|
1727
|
+
comment.start_column,
|
|
1728
|
+
comment.end_column
|
|
1729
|
+
);
|
|
1730
|
+
if (textAtOrigin === comment.selected_text) return false;
|
|
1731
|
+
return true;
|
|
1732
|
+
}
|
|
1688
1733
|
function getLineShift2(diffHunks, line) {
|
|
1689
1734
|
let shift = 0;
|
|
1690
1735
|
let modified = false;
|
|
@@ -1708,6 +1753,7 @@ var import_node_path6 = __toESM(require("node:path"), 1);
|
|
|
1708
1753
|
async function reanchorDocument(doc, documentLines, opts = {}) {
|
|
1709
1754
|
const results = [];
|
|
1710
1755
|
const threshold = opts.threshold ?? DEFAULT_THRESHOLD;
|
|
1756
|
+
const proximityWindow = opts.proximityWindow;
|
|
1711
1757
|
let diffHunks;
|
|
1712
1758
|
let commitIsStale = false;
|
|
1713
1759
|
if (!opts.noGit && await isGitAvailable()) {
|
|
@@ -1724,19 +1770,20 @@ async function reanchorDocument(doc, documentLines, opts = {}) {
|
|
|
1724
1770
|
const result = reanchorComment(comment, documentLines, {
|
|
1725
1771
|
diffHunks: hunks,
|
|
1726
1772
|
threshold,
|
|
1727
|
-
commitIsStale: true
|
|
1773
|
+
commitIsStale: true,
|
|
1774
|
+
proximityWindow
|
|
1728
1775
|
});
|
|
1729
1776
|
results.push(result);
|
|
1730
1777
|
continue;
|
|
1731
1778
|
}
|
|
1732
1779
|
results.push(
|
|
1733
|
-
reanchorComment(comment, documentLines, { threshold, commitIsStale: false })
|
|
1780
|
+
reanchorComment(comment, documentLines, { threshold, commitIsStale: false, proximityWindow })
|
|
1734
1781
|
);
|
|
1735
1782
|
}
|
|
1736
1783
|
return results;
|
|
1737
1784
|
}
|
|
1738
1785
|
}
|
|
1739
|
-
return reanchorDocumentLines(doc, documentLines, { threshold });
|
|
1786
|
+
return reanchorDocumentLines(doc, documentLines, { threshold, proximityWindow });
|
|
1740
1787
|
}
|
|
1741
1788
|
async function reanchorFile(sidecarPath, opts = {}) {
|
|
1742
1789
|
const doc = await parseSidecar(sidecarPath);
|