@imenam/mcp-documentor 1.0.72 → 1.0.74
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/src/core/conflict-inspector.d.ts +13 -0
- package/dist/src/core/conflict-inspector.js +17 -0
- package/dist/src/core/git-sync.d.ts +11 -4
- package/dist/src/core/git-sync.js +33 -22
- package/dist/src/index.js +1 -4
- package/dist/src/tools/conflicts.js +50 -5
- package/dist/src/tools/sync.js +14 -7
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/gui/dist/assets/{arc-KWKRz6LO.js → arc-BNuZ3dSL.js} +1 -1
- package/gui/dist/assets/{architectureDiagram-3BPJPVTR-Cjfl98ZS.js → architectureDiagram-3BPJPVTR-D8g3tPQx.js} +1 -1
- package/gui/dist/assets/{blockDiagram-GPEHLZMM-CtPUrnse.js → blockDiagram-GPEHLZMM-limhup8_.js} +1 -1
- package/gui/dist/assets/{c4Diagram-AAUBKEIU-Dh5_7IZy.js → c4Diagram-AAUBKEIU-Ct39JMIy.js} +1 -1
- package/gui/dist/assets/channel-uoEIgtym.js +1 -0
- package/gui/dist/assets/{chunk-2J33WTMH-44T5JbXy.js → chunk-2J33WTMH-BkLJTezj.js} +1 -1
- package/gui/dist/assets/{chunk-4BX2VUAB-D1fEQ0Ng.js → chunk-4BX2VUAB-CR5NbhDR.js} +1 -1
- package/gui/dist/assets/{chunk-55IACEB6-M2lEB2Vn.js → chunk-55IACEB6-CtQYMnJS.js} +1 -1
- package/gui/dist/assets/{chunk-727SXJPM-B3A_Fp4f.js → chunk-727SXJPM-B2q7tUn8.js} +1 -1
- package/gui/dist/assets/{chunk-AQP2D5EJ-CqkJXv3c.js → chunk-AQP2D5EJ-Bbcnrfrp.js} +1 -1
- package/gui/dist/assets/{chunk-FMBD7UC4-Iyj_Ky8e.js → chunk-FMBD7UC4-D8gd4zRe.js} +1 -1
- package/gui/dist/assets/{chunk-ND2GUHAM-DE5R-C9U.js → chunk-ND2GUHAM-DiZKzZtG.js} +1 -1
- package/gui/dist/assets/{chunk-QZHKN3VN-BekBU-YP.js → chunk-QZHKN3VN-CrKx-e2_.js} +1 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-DmhYb54l.js +1 -0
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-DmhYb54l.js +1 -0
- package/gui/dist/assets/{cose-bilkent-S5V4N54A-Dw2zOlxr.js → cose-bilkent-S5V4N54A-BDUCVkTl.js} +1 -1
- package/gui/dist/assets/{dagre-BM42HDAG-ebVZNyxI.js → dagre-BM42HDAG-DkoYBHXK.js} +1 -1
- package/gui/dist/assets/{diagram-2AECGRRQ-Bh5FUKy0.js → diagram-2AECGRRQ-BT1YJHon.js} +1 -1
- package/gui/dist/assets/{diagram-5GNKFQAL-YJJTJy-Q.js → diagram-5GNKFQAL-C00r6F35.js} +1 -1
- package/gui/dist/assets/{diagram-KO2AKTUF-D161kBvB.js → diagram-KO2AKTUF-B4wcN5a_.js} +1 -1
- package/gui/dist/assets/{diagram-LMA3HP47-Drcmx9J8.js → diagram-LMA3HP47-B9FqCfel.js} +1 -1
- package/gui/dist/assets/{diagram-OG6HWLK6-DRrxnW_H.js → diagram-OG6HWLK6-COPCDzdG.js} +1 -1
- package/gui/dist/assets/{erDiagram-TEJ5UH35-FrH4iEa3.js → erDiagram-TEJ5UH35-BGQGT2wN.js} +1 -1
- package/gui/dist/assets/{flowDiagram-I6XJVG4X-BMxSElvH.js → flowDiagram-I6XJVG4X-A7jKWUpi.js} +1 -1
- package/gui/dist/assets/{ganttDiagram-6RSMTGT7-H09FGGUr.js → ganttDiagram-6RSMTGT7-NhFkpY-x.js} +1 -1
- package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-DLhPfXNp.js → gitGraphDiagram-PVQCEYII-z0AqyYG4.js} +1 -1
- package/gui/dist/assets/{index-CdGsMXYw.js → index-Dub5OLKk.js} +95 -95
- package/gui/dist/assets/index-QkCDSQ2A.css +1 -0
- package/gui/dist/assets/{infoDiagram-5YYISTIA-CV8ctX8t.js → infoDiagram-5YYISTIA-D69UR9u9.js} +1 -1
- package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-BeuHUSPO.js → ishikawaDiagram-YF4QCWOH-DSTqIoro.js} +1 -1
- package/gui/dist/assets/{journeyDiagram-JHISSGLW-CfAHwYXQ.js → journeyDiagram-JHISSGLW-CekXdcoE.js} +1 -1
- package/gui/dist/assets/{kanban-definition-UN3LZRKU-CFF4KWbn.js → kanban-definition-UN3LZRKU-CseGvFK9.js} +1 -1
- package/gui/dist/assets/{linear-DwZpGeRp.js → linear-BtVl_9_C.js} +1 -1
- package/gui/dist/assets/{mindmap-definition-RKZ34NQL-BW0yW4ap.js → mindmap-definition-RKZ34NQL-XLjQIU37.js} +1 -1
- package/gui/dist/assets/{pieDiagram-4H26LBE5-plBCN_c7.js → pieDiagram-4H26LBE5-BcvUzWoW.js} +1 -1
- package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-Dmp9sOq_.js → quadrantDiagram-W4KKPZXB-BzoYEmNW.js} +1 -1
- package/gui/dist/assets/{requirementDiagram-4Y6WPE33-CthKC15J.js → requirementDiagram-4Y6WPE33-icKC1_Nd.js} +1 -1
- package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-BCAzzfPU.js → sankeyDiagram-5OEKKPKP-mi3i-2dZ.js} +1 -1
- package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-DgwOe82k.js → sequenceDiagram-3UESZ5HK-XrcDkRlM.js} +1 -1
- package/gui/dist/assets/{stateDiagram-AJRCARHV-B6k-x1TC.js → stateDiagram-AJRCARHV-UXaqrCNT.js} +1 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-Bhdu4lv4.js +1 -0
- package/gui/dist/assets/{timeline-definition-PNZ67QCA-CqwqJkZw.js → timeline-definition-PNZ67QCA-yd6607p6.js} +1 -1
- package/gui/dist/assets/{vennDiagram-CIIHVFJN-REFDrI5t.js → vennDiagram-CIIHVFJN-BsRqUcIp.js} +1 -1
- package/gui/dist/assets/{wardley-L42UT6IY-53gLZWfR.js → wardley-L42UT6IY-BmdA7yk0.js} +1 -1
- package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-OPfKpNx-.js → wardleyDiagram-YWT4CUSO-CRdmckD8.js} +1 -1
- package/gui/dist/assets/{xychartDiagram-2RQKCTM6-HWGySgFg.js → xychartDiagram-2RQKCTM6-DWpNIQY-.js} +1 -1
- package/gui/dist/index.html +2 -2
- package/package.json +65 -65
- package/gui/dist/assets/channel-JejxY436.js +0 -1
- package/gui/dist/assets/classDiagram-4FO5ZUOK-CdAqI_QA.js +0 -1
- package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-CdAqI_QA.js +0 -1
- package/gui/dist/assets/index-DAzSbGj_.css +0 -1
- package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-bfw3SR0t.js +0 -1
|
@@ -20,6 +20,19 @@ export interface ConflictHunk {
|
|
|
20
20
|
remote: string[];
|
|
21
21
|
/** Common ancestor, present only when the markers were written in diff3 style. */
|
|
22
22
|
base?: string[];
|
|
23
|
+
/**
|
|
24
|
+
* The hunk concerns frontmatter, either because it sits inside the YAML block
|
|
25
|
+
* or because the doc merge driver hoisted a frontmatter disagreement to the
|
|
26
|
+
* top of the body. Keeping both sides of one of these would duplicate a key,
|
|
27
|
+
* so that choice is refused rather than silently producing broken YAML.
|
|
28
|
+
*/
|
|
29
|
+
inFrontmatter: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* The hunk is the block the doc merge driver hoisted out of the frontmatter.
|
|
32
|
+
* Its sides are `key: value` lines that belong back in the YAML, not in the
|
|
33
|
+
* body where the markers physically sit.
|
|
34
|
+
*/
|
|
35
|
+
hoisted: boolean;
|
|
23
36
|
}
|
|
24
37
|
export interface ParsedFile {
|
|
25
38
|
lines: string[];
|
|
@@ -34,6 +34,19 @@ export function hasConflictMarkers(text) {
|
|
|
34
34
|
function sidesFor(operation, first, second) {
|
|
35
35
|
return operation === "rebase" ? { local: second, remote: first } : { local: first, remote: second };
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Line range of the YAML frontmatter block, if the file opens with one.
|
|
39
|
+
* Returns null when there is none.
|
|
40
|
+
*/
|
|
41
|
+
function frontmatterRange(lines) {
|
|
42
|
+
if (lines.length === 0 || lines[0].trim() !== "---")
|
|
43
|
+
return null;
|
|
44
|
+
for (let i = 1; i < lines.length; i++) {
|
|
45
|
+
if (lines[i].trim() === "---")
|
|
46
|
+
return { start: 0, end: i };
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
37
50
|
function sectionFor(lines, hunkStart) {
|
|
38
51
|
for (let i = hunkStart - 1; i >= 0; i--) {
|
|
39
52
|
const match = lines[i].match(HEADING);
|
|
@@ -45,6 +58,7 @@ function sectionFor(lines, hunkStart) {
|
|
|
45
58
|
/** Splits a conflicted file into hunks. Handles both diff3 and plain markers. */
|
|
46
59
|
export function parseConflicts(text, operation) {
|
|
47
60
|
const lines = splitLines(text);
|
|
61
|
+
const frontmatter = frontmatterRange(lines);
|
|
48
62
|
const hunks = [];
|
|
49
63
|
for (let i = 0; i < lines.length; i++) {
|
|
50
64
|
if (!START.test(lines[i]))
|
|
@@ -56,6 +70,7 @@ export function parseConflicts(text, operation) {
|
|
|
56
70
|
let sawBase = false;
|
|
57
71
|
let closed = false;
|
|
58
72
|
let j = i + 1;
|
|
73
|
+
const label = lines[i].match(START)?.[1] ?? "";
|
|
59
74
|
for (; j < lines.length; j++) {
|
|
60
75
|
const line = lines[j];
|
|
61
76
|
if (BASE.test(line)) {
|
|
@@ -84,6 +99,8 @@ export function parseConflicts(text, operation) {
|
|
|
84
99
|
local,
|
|
85
100
|
remote,
|
|
86
101
|
...(sawBase ? { base } : {}),
|
|
102
|
+
inFrontmatter: label.includes("frontmatter") || (frontmatter !== null && i >= frontmatter.start && i <= frontmatter.end),
|
|
103
|
+
hoisted: label.includes("frontmatter"),
|
|
87
104
|
});
|
|
88
105
|
i = j;
|
|
89
106
|
}
|
|
@@ -44,13 +44,11 @@ export type PullResult = {
|
|
|
44
44
|
} | {
|
|
45
45
|
status: "updated";
|
|
46
46
|
changed: string[];
|
|
47
|
-
|
|
48
|
-
status: "diverged";
|
|
49
|
-
ahead: number;
|
|
50
|
-
behind: number;
|
|
47
|
+
autoCommitted?: boolean;
|
|
51
48
|
} | {
|
|
52
49
|
status: "conflict";
|
|
53
50
|
conflicted: string[];
|
|
51
|
+
autoCommitted?: boolean;
|
|
54
52
|
};
|
|
55
53
|
export declare class SyncEngine {
|
|
56
54
|
private storage;
|
|
@@ -88,6 +86,15 @@ export declare class SyncEngine {
|
|
|
88
86
|
*/
|
|
89
87
|
pull(): Promise<PullResult>;
|
|
90
88
|
private diffNames;
|
|
89
|
+
/**
|
|
90
|
+
* Stages and commits whatever the working tree holds. Committing is what makes
|
|
91
|
+
* local edits safe to rebase over, so both push and pull go through here
|
|
92
|
+
* rather than asking the caller to tidy up first.
|
|
93
|
+
*
|
|
94
|
+
* .gitignore (written by ensureRepo) excludes all derived and machine-local
|
|
95
|
+
* state, so `add -A` only ever stages versioned files.
|
|
96
|
+
*/
|
|
97
|
+
private commitLocalChanges;
|
|
91
98
|
/** Stages versioned files, commits if there is anything to commit, and pushes. */
|
|
92
99
|
push(message?: string): Promise<{
|
|
93
100
|
pushed: boolean;
|
|
@@ -205,20 +205,23 @@ export class SyncEngine {
|
|
|
205
205
|
}
|
|
206
206
|
// Ensure upstream tracking so status ahead/behind is meaningful.
|
|
207
207
|
await this.git.branch(["--set-upstream-to", `origin/${cfg.branch}`, cfg.branch]).catch(() => { });
|
|
208
|
-
|
|
209
|
-
if (status.behind === 0)
|
|
208
|
+
let status = await this.git.status();
|
|
209
|
+
if (status.behind === 0 && status.files.length === 0)
|
|
210
210
|
return { status: "up-to-date" };
|
|
211
|
-
// Uncommitted local
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
211
|
+
// Uncommitted local edits cannot be rebased over, and reporting the
|
|
212
|
+
// divergence instead used to strand the caller: the pull asked for a push,
|
|
213
|
+
// which the remote rejected and which asked for a pull. Committing them
|
|
214
|
+
// resolves it - the edits become a commit the rebase can replay, nothing is
|
|
215
|
+
// published, and nothing can be clobbered.
|
|
216
|
+
const autoCommitted = status.files.length > 0 ? await this.commitLocalChanges() : false;
|
|
217
|
+
if (autoCommitted)
|
|
218
|
+
status = await this.git.status();
|
|
216
219
|
const before = await this.git.revparse(["HEAD"]).catch(() => "");
|
|
217
220
|
// Pure behind → fast-forward.
|
|
218
221
|
if (status.ahead === 0) {
|
|
219
222
|
await this.git.merge(["--ff-only", `origin/${cfg.branch}`]);
|
|
220
223
|
const after = await this.git.revparse(["HEAD"]).catch(() => "");
|
|
221
|
-
return { status: "updated", changed: await this.diffNames(before, after) };
|
|
224
|
+
return { status: "updated", changed: await this.diffNames(before, after), autoCommitted };
|
|
222
225
|
}
|
|
223
226
|
// Diverged with a clean tree → replay local commits on top of the remote. A
|
|
224
227
|
// conflict stops the rebase with both versions preserved in the index, so it
|
|
@@ -229,17 +232,35 @@ export class SyncEngine {
|
|
|
229
232
|
catch {
|
|
230
233
|
const after = await this.git.status();
|
|
231
234
|
if (after.conflicted.length > 0)
|
|
232
|
-
return { status: "conflict", conflicted: after.conflicted };
|
|
235
|
+
return { status: "conflict", conflicted: after.conflicted, autoCommitted };
|
|
233
236
|
throw new Error("Rebase failed without recorded conflicts. Inspect the data directory, or run `git rebase --abort` there to get back to the previous state.");
|
|
234
237
|
}
|
|
235
238
|
const rebased = await this.git.revparse(["HEAD"]).catch(() => "");
|
|
236
|
-
return { status: "updated", changed: await this.diffNames(before, rebased) };
|
|
239
|
+
return { status: "updated", changed: await this.diffNames(before, rebased), autoCommitted };
|
|
237
240
|
}
|
|
238
241
|
async diffNames(before, after) {
|
|
239
242
|
if (!before || !after || before === after)
|
|
240
243
|
return [];
|
|
241
244
|
return (await this.git.raw(["diff", "--name-only", before, after])).split("\n").filter(Boolean);
|
|
242
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Stages and commits whatever the working tree holds. Committing is what makes
|
|
248
|
+
* local edits safe to rebase over, so both push and pull go through here
|
|
249
|
+
* rather than asking the caller to tidy up first.
|
|
250
|
+
*
|
|
251
|
+
* .gitignore (written by ensureRepo) excludes all derived and machine-local
|
|
252
|
+
* state, so `add -A` only ever stages versioned files.
|
|
253
|
+
*/
|
|
254
|
+
async commitLocalChanges(message) {
|
|
255
|
+
await this.git.add(["-A"]);
|
|
256
|
+
const status = await this.git.status();
|
|
257
|
+
const hasWork = status.staged.length > 0 || status.created.length > 0 || status.deleted.length > 0 || status.modified.length > 0;
|
|
258
|
+
if (!hasWork)
|
|
259
|
+
return false;
|
|
260
|
+
const machine = process.env.COMPUTERNAME || process.env.HOSTNAME || "unknown-machine";
|
|
261
|
+
await this.git.commit(message?.trim() || `docs sync from ${machine} · ${new Date().toISOString()}`);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
243
264
|
/** Stages versioned files, commits if there is anything to commit, and pushes. */
|
|
244
265
|
async push(message) {
|
|
245
266
|
const cfg = this.requireConfig();
|
|
@@ -252,24 +273,14 @@ export class SyncEngine {
|
|
|
252
273
|
throw new Error(`A ${operation === "none" ? "conflict" : operation} is still unresolved (${pending.conflicted.length} conflicted file(s)). ` +
|
|
253
274
|
"Resolve it with resolve_sync_conflict before pushing, otherwise the conflict markers would be committed as documentation content.");
|
|
254
275
|
}
|
|
255
|
-
|
|
256
|
-
// so -A safely stages only versioned files (projects/, config.json, .gitignore).
|
|
257
|
-
await this.git.add(["-A"]);
|
|
258
|
-
const status = await this.git.status();
|
|
259
|
-
let committed = false;
|
|
260
|
-
if (status.staged.length > 0 || status.created.length > 0 || status.deleted.length > 0 || status.modified.length > 0) {
|
|
261
|
-
const machine = process.env.COMPUTERNAME || process.env.HOSTNAME || "unknown-machine";
|
|
262
|
-
const msg = message?.trim() || `docs sync from ${machine} · ${new Date().toISOString()}`;
|
|
263
|
-
await this.git.commit(msg);
|
|
264
|
-
committed = true;
|
|
265
|
-
}
|
|
276
|
+
const committed = await this.commitLocalChanges(message);
|
|
266
277
|
try {
|
|
267
278
|
await this.git.push(["-u", "origin", `HEAD:${cfg.branch}`]);
|
|
268
279
|
}
|
|
269
280
|
catch (e) {
|
|
270
281
|
const msg = e instanceof Error ? e.message : String(e);
|
|
271
282
|
if (/fetch first|non-fast-forward|\[rejected\]/i.test(msg)) {
|
|
272
|
-
throw new Error("Push rejected: the remote has commits you don't have locally. Run
|
|
283
|
+
throw new Error("Push rejected: the remote has commits you don't have locally. Run sync_docs, which integrates them and pushes in one step.");
|
|
273
284
|
}
|
|
274
285
|
throw e;
|
|
275
286
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -1362,7 +1362,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1362
1362
|
type: "object",
|
|
1363
1363
|
properties: {
|
|
1364
1364
|
hunk: { type: "number", description: "Hunk number from get_conflict_hunks" },
|
|
1365
|
-
take: { type: "string", enum: ["local", "remote", "both", "none", "custom"], description: "'local' this machine, 'remote' the other machine, 'both' concatenates local then remote, 'none' drops the passage, 'custom' uses the provided content" },
|
|
1365
|
+
take: { type: "string", enum: ["local", "remote", "both", "none", "custom"], description: "'local' this machine, 'remote' the other machine, 'both' concatenates local then remote, 'none' drops the passage, 'custom' uses the provided content. 'both' is refused on frontmatter hunks, where it would duplicate a YAML key." },
|
|
1366
1366
|
content: { type: "string", description: "Replacement text, required when take is 'custom'" },
|
|
1367
1367
|
},
|
|
1368
1368
|
required: ["hunk", "take"],
|
|
@@ -1505,9 +1505,6 @@ async function syncPullOnStartup() {
|
|
|
1505
1505
|
await reindexEmbeddings(storageEngine, embeddingEngine, embeddingStore, {});
|
|
1506
1506
|
}
|
|
1507
1507
|
}
|
|
1508
|
-
else if (res.status === "diverged") {
|
|
1509
|
-
logger.warn(`Startup sync: local history diverged (ahead ${res.ahead}, behind ${res.behind}). Started on local data — run sync_status / sync_docs to reconcile.`);
|
|
1510
|
-
}
|
|
1511
1508
|
else if (res.status === "conflict") {
|
|
1512
1509
|
logger.warn(`Startup sync: merge conflict in ${res.conflicted.length} file(s). Started on local data — resolve with resolve_sync_conflict.`);
|
|
1513
1510
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import matter from "gray-matter";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { parseConflicts, applyResolutions, preview, hasConflictMarkers } from "../core/conflict-inspector.js";
|
|
3
4
|
const ok = (text) => ({ content: [{ type: "text", text }] });
|
|
@@ -73,7 +74,8 @@ export async function getConflictHunks(syncEngine, args) {
|
|
|
73
74
|
return ok(`None of the requested hunks exist in "${v.path}" (it has ${hunks.length}).`);
|
|
74
75
|
const lines = [`${v.path} — ${wanted.length} of ${hunks.length} hunk(s), during a ${operation}.`, ""];
|
|
75
76
|
for (const hunk of wanted) {
|
|
76
|
-
|
|
77
|
+
const scope = hunk.inFrontmatter ? " [frontmatter — \"both\" is not available, it would duplicate a key]" : "";
|
|
78
|
+
lines.push(`Hunk ${hunk.index} — section "${hunk.section}" (line ${hunk.line})${scope}`);
|
|
77
79
|
if (hunk.base)
|
|
78
80
|
lines.push(` [base — what both sides started from]`, preview(hunk.base, v.max_chars_per_side));
|
|
79
81
|
lines.push(` [local — this machine]`, preview(hunk.local, v.max_chars_per_side));
|
|
@@ -112,10 +114,53 @@ export async function resolveConflictHunks(syncEngine, args) {
|
|
|
112
114
|
if (!hasConflictMarkers(original)) {
|
|
113
115
|
return err(`"${v.path}" has no conflict markers — there is nothing to resolve hunk by hunk.`);
|
|
114
116
|
}
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
// Concatenating both sides of a frontmatter hunk repeats the key it holds,
|
|
118
|
+
// which makes the YAML unparseable for every reader of the document.
|
|
119
|
+
const { hunks } = parseConflicts(original, operation);
|
|
120
|
+
const badBoth = v.decisions.filter((d) => d.take === "both" && hunks.find((h) => h.index === d.hunk)?.inFrontmatter);
|
|
121
|
+
if (badBoth.length > 0) {
|
|
122
|
+
return err(`Hunk(s) ${badBoth.map((d) => d.hunk).join(", ")} concern frontmatter, where "both" would duplicate a key and break the YAML. ` +
|
|
123
|
+
`Use "local", "remote", or "custom" with the single value you want.`);
|
|
124
|
+
}
|
|
125
|
+
// A hoisted frontmatter block lives in the body but describes YAML keys.
|
|
126
|
+
// Applying its chosen side literally would paste "title: X" into the prose
|
|
127
|
+
// and leave the real frontmatter untouched, so those hunks are dropped from
|
|
128
|
+
// the body and their values folded back into the frontmatter instead.
|
|
129
|
+
const frontmatterUpdates = {};
|
|
130
|
+
const effective = decisions.map((decision) => {
|
|
131
|
+
const hunk = hunks.find((h) => h.index === decision.index);
|
|
132
|
+
if (!hunk?.hoisted)
|
|
133
|
+
return decision;
|
|
134
|
+
const chosen = decision.take === "local" ? hunk.local
|
|
135
|
+
: decision.take === "remote" ? hunk.remote
|
|
136
|
+
: decision.take === "custom" ? decision.content.split(/\r?\n/)
|
|
137
|
+
: [];
|
|
138
|
+
if (chosen.length > 0) {
|
|
139
|
+
try {
|
|
140
|
+
const asYaml = ["---", ...chosen, "---", ""].join("\n");
|
|
141
|
+
Object.assign(frontmatterUpdates, matter(asYaml).data);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Not parseable as YAML - leave the frontmatter alone rather than
|
|
145
|
+
// corrupting it; the caller still sees the block disappear.
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return { index: decision.index, take: "none" };
|
|
149
|
+
});
|
|
150
|
+
const result = applyResolutions(original, operation, effective);
|
|
151
|
+
if (Object.keys(frontmatterUpdates).length > 0) {
|
|
152
|
+
try {
|
|
153
|
+
const parsed = matter(result.text);
|
|
154
|
+
result.text = matter.stringify(parsed.content, { ...parsed.data, ...frontmatterUpdates });
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Frontmatter still holds markers of its own; it will be rewritten
|
|
158
|
+
// once those hunks are resolved too.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// The invariant is checked against the resulting text rather than the hunk
|
|
162
|
+
// bookkeeping: staging a file that still holds markers tells git the
|
|
163
|
+
// conflict is settled and lets them be committed as documentation content.
|
|
119
164
|
const settled = !hasConflictMarkers(result.text);
|
|
120
165
|
await syncEngine.writeResolvedFile(v.path, result.text, settled);
|
|
121
166
|
const lines = [`Resolved ${result.applied.length} hunk(s) in "${v.path}".`];
|
package/dist/src/tools/sync.js
CHANGED
|
@@ -34,16 +34,23 @@ export async function syncStatus(syncEngine) {
|
|
|
34
34
|
return err(`Error getting sync status: ${e instanceof Error ? e.message : String(e)}`);
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* A pull commits uncommitted local edits before rebasing over them - worth
|
|
39
|
+
* saying, since the caller did not ask for a commit.
|
|
40
|
+
*/
|
|
41
|
+
function autoCommitNote(autoCommitted) {
|
|
42
|
+
return autoCommitted
|
|
43
|
+
? "Your uncommitted local changes were committed first so they could be replayed on top of the remote.\n"
|
|
44
|
+
: "";
|
|
45
|
+
}
|
|
37
46
|
function describePull(res) {
|
|
38
47
|
switch (res.status) {
|
|
39
48
|
case "up-to-date":
|
|
40
49
|
return "Already up to date — nothing to pull.";
|
|
41
50
|
case "updated":
|
|
42
|
-
return
|
|
43
|
-
case "diverged":
|
|
44
|
-
return `You have uncommitted local changes while the remote has ${res.behind} new commit(s). No changes applied — your local data is intact. Run push_docs to commit and publish your changes, then pull again.`;
|
|
51
|
+
return `${autoCommitNote(res.autoCommitted)}Pulled ${res.changed.length} changed file(s):\n - ${res.changed.join("\n - ")}`;
|
|
45
52
|
case "conflict":
|
|
46
|
-
return
|
|
53
|
+
return `${autoCommitNote(res.autoCommitted)}Conflict in ${res.conflicted.length} file(s):\n - ${res.conflicted.join("\n - ")}\nResolve with resolve_sync_conflict (side "local" keeps this machine version, "remote" keeps the other machine version), or edit these markdown files directly. Then run push_docs.`;
|
|
47
54
|
}
|
|
48
55
|
}
|
|
49
56
|
/** Reindex embeddings for new docs after a successful merge. Best-effort. */
|
|
@@ -92,9 +99,9 @@ export async function syncDocs(syncEngine, storageEngine, embeddingEngine, embed
|
|
|
92
99
|
const v = PushDocsArgs.parse(args);
|
|
93
100
|
const pull = await syncEngine.pull();
|
|
94
101
|
const reindexNote = await reindexAfterPull(pull, storageEngine, embeddingEngine, embeddingStore);
|
|
95
|
-
// Don't push over a
|
|
96
|
-
if (pull.status === "
|
|
97
|
-
return ok(`Pull step:\n${describePull(pull)}${reindexNote}\n\nPush skipped until the
|
|
102
|
+
// Don't push over a conflict — surface it so the caller can resolve first.
|
|
103
|
+
if (pull.status === "conflict") {
|
|
104
|
+
return ok(`Pull step:\n${describePull(pull)}${reindexNote}\n\nPush skipped until the conflict is resolved.`);
|
|
98
105
|
}
|
|
99
106
|
const push = await syncEngine.push(v.message);
|
|
100
107
|
return ok(`Pull step: ${describePull(pull)}${reindexNote}\n\nPush step: ${push.message}`);
|
package/dist/src/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const APP_VERSION = "1.0.
|
|
1
|
+
export declare const APP_VERSION = "1.0.74";
|
package/dist/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated version file - do not edit manually
|
|
2
|
-
export const APP_VERSION = '1.0.
|
|
2
|
+
export const APP_VERSION = '1.0.74';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-
|
|
1
|
+
import{a0 as ln,a1 as an,a2 as y,a3 as tn,a4 as H,a5 as q,a6 as _,a7 as un,a8 as rn,a9 as L,aa as o,ab as B,ac as sn,ad as on,ae as fn}from"./index-Dub5OLKk.js";function cn(l){return l.innerRadius}function yn(l){return l.outerRadius}function gn(l){return l.startAngle}function dn(l){return l.endAngle}function mn(l){return l&&l.padAngle}function pn(l,h,I,D,v,A,C,a){var O=I-l,i=D-h,n=C-v,d=a-A,u=d*O-n*i;if(!(u*u<y))return u=(n*(h-A)-d*(l-v))/u,[l+u*O,h+u*i]}function W(l,h,I,D,v,A,C){var a=l-I,O=h-D,i=(C?A:-A)/L(a*a+O*O),n=i*O,d=-i*a,u=l+n,s=h+d,f=I+n,c=D+d,F=(u+f)/2,t=(s+c)/2,m=f-u,g=c-s,R=m*m+g*g,T=v-A,P=u*c-f*s,S=(g<0?-1:1)*L(fn(0,T*T*R-P*P)),j=(P*g-m*S)/R,z=(-P*m-g*S)/R,w=(P*g+m*S)/R,p=(-P*m+g*S)/R,x=j-F,e=z-t,r=w-F,G=p-t;return x*x+e*e>r*r+G*G&&(j=w,z=p),{cx:j,cy:z,x01:-n,y01:-d,x11:j*(v/T-1),y11:z*(v/T-1)}}function hn(){var l=cn,h=yn,I=B(0),D=null,v=gn,A=dn,C=mn,a=null,O=ln(i);function i(){var n,d,u=+l.apply(this,arguments),s=+h.apply(this,arguments),f=v.apply(this,arguments)-an,c=A.apply(this,arguments)-an,F=un(c-f),t=c>f;if(a||(a=n=O()),s<u&&(d=s,s=u,u=d),!(s>y))a.moveTo(0,0);else if(F>tn-y)a.moveTo(s*H(f),s*q(f)),a.arc(0,0,s,f,c,!t),u>y&&(a.moveTo(u*H(c),u*q(c)),a.arc(0,0,u,c,f,t));else{var m=f,g=c,R=f,T=c,P=F,S=F,j=C.apply(this,arguments)/2,z=j>y&&(D?+D.apply(this,arguments):L(u*u+s*s)),w=_(un(s-u)/2,+I.apply(this,arguments)),p=w,x=w,e,r;if(z>y){var G=sn(z/u*q(j)),M=sn(z/s*q(j));(P-=G*2)>y?(G*=t?1:-1,R+=G,T-=G):(P=0,R=T=(f+c)/2),(S-=M*2)>y?(M*=t?1:-1,m+=M,g-=M):(S=0,m=g=(f+c)/2)}var J=s*H(m),K=s*q(m),N=u*H(T),Q=u*q(T);if(w>y){var U=s*H(g),V=s*q(g),X=u*H(R),Y=u*q(R),E;if(F<rn)if(E=pn(J,K,X,Y,U,V,N,Q)){var Z=J-E[0],$=K-E[1],b=U-E[0],k=V-E[1],nn=1/q(on((Z*b+$*k)/(L(Z*Z+$*$)*L(b*b+k*k)))/2),en=L(E[0]*E[0]+E[1]*E[1]);p=_(w,(u-en)/(nn-1)),x=_(w,(s-en)/(nn+1))}else p=x=0}S>y?x>y?(e=W(X,Y,J,K,s,x,t),r=W(U,V,N,Q,s,x,t),a.moveTo(e.cx+e.x01,e.cy+e.y01),x<w?a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,x,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,s,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),!t),a.arc(r.cx,r.cy,x,o(r.y11,r.x11),o(r.y01,r.x01),!t))):(a.moveTo(J,K),a.arc(0,0,s,m,g,!t)):a.moveTo(J,K),!(u>y)||!(P>y)?a.lineTo(N,Q):p>y?(e=W(N,Q,U,V,u,-p,t),r=W(J,K,X,Y,u,-p,t),a.lineTo(e.cx+e.x01,e.cy+e.y01),p<w?a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(r.y01,r.x01),!t):(a.arc(e.cx,e.cy,p,o(e.y01,e.x01),o(e.y11,e.x11),!t),a.arc(0,0,u,o(e.cy+e.y11,e.cx+e.x11),o(r.cy+r.y11,r.cx+r.x11),t),a.arc(r.cx,r.cy,p,o(r.y11,r.x11),o(r.y01,r.x01),!t))):a.arc(0,0,u,T,R,t)}if(a.closePath(),n)return a=null,n+""||null}return i.centroid=function(){var n=(+l.apply(this,arguments)+ +h.apply(this,arguments))/2,d=(+v.apply(this,arguments)+ +A.apply(this,arguments))/2-rn/2;return[H(d)*n,q(d)*n]},i.innerRadius=function(n){return arguments.length?(l=typeof n=="function"?n:B(+n),i):l},i.outerRadius=function(n){return arguments.length?(h=typeof n=="function"?n:B(+n),i):h},i.cornerRadius=function(n){return arguments.length?(I=typeof n=="function"?n:B(+n),i):I},i.padRadius=function(n){return arguments.length?(D=n==null?null:typeof n=="function"?n:B(+n),i):D},i.startAngle=function(n){return arguments.length?(v=typeof n=="function"?n:B(+n),i):v},i.endAngle=function(n){return arguments.length?(A=typeof n=="function"?n:B(+n),i):A},i.padAngle=function(n){return arguments.length?(C=typeof n=="function"?n:B(+n),i):C},i.context=function(n){return arguments.length?(a=n??null,i):a},i}export{hn as d};
|