@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.
Files changed (62) hide show
  1. package/dist/src/core/conflict-inspector.d.ts +13 -0
  2. package/dist/src/core/conflict-inspector.js +17 -0
  3. package/dist/src/core/git-sync.d.ts +11 -4
  4. package/dist/src/core/git-sync.js +33 -22
  5. package/dist/src/index.js +1 -4
  6. package/dist/src/tools/conflicts.js +50 -5
  7. package/dist/src/tools/sync.js +14 -7
  8. package/dist/src/version.d.ts +1 -1
  9. package/dist/src/version.js +1 -1
  10. package/gui/dist/assets/{arc-KWKRz6LO.js → arc-BNuZ3dSL.js} +1 -1
  11. package/gui/dist/assets/{architectureDiagram-3BPJPVTR-Cjfl98ZS.js → architectureDiagram-3BPJPVTR-D8g3tPQx.js} +1 -1
  12. package/gui/dist/assets/{blockDiagram-GPEHLZMM-CtPUrnse.js → blockDiagram-GPEHLZMM-limhup8_.js} +1 -1
  13. package/gui/dist/assets/{c4Diagram-AAUBKEIU-Dh5_7IZy.js → c4Diagram-AAUBKEIU-Ct39JMIy.js} +1 -1
  14. package/gui/dist/assets/channel-uoEIgtym.js +1 -0
  15. package/gui/dist/assets/{chunk-2J33WTMH-44T5JbXy.js → chunk-2J33WTMH-BkLJTezj.js} +1 -1
  16. package/gui/dist/assets/{chunk-4BX2VUAB-D1fEQ0Ng.js → chunk-4BX2VUAB-CR5NbhDR.js} +1 -1
  17. package/gui/dist/assets/{chunk-55IACEB6-M2lEB2Vn.js → chunk-55IACEB6-CtQYMnJS.js} +1 -1
  18. package/gui/dist/assets/{chunk-727SXJPM-B3A_Fp4f.js → chunk-727SXJPM-B2q7tUn8.js} +1 -1
  19. package/gui/dist/assets/{chunk-AQP2D5EJ-CqkJXv3c.js → chunk-AQP2D5EJ-Bbcnrfrp.js} +1 -1
  20. package/gui/dist/assets/{chunk-FMBD7UC4-Iyj_Ky8e.js → chunk-FMBD7UC4-D8gd4zRe.js} +1 -1
  21. package/gui/dist/assets/{chunk-ND2GUHAM-DE5R-C9U.js → chunk-ND2GUHAM-DiZKzZtG.js} +1 -1
  22. package/gui/dist/assets/{chunk-QZHKN3VN-BekBU-YP.js → chunk-QZHKN3VN-CrKx-e2_.js} +1 -1
  23. package/gui/dist/assets/classDiagram-4FO5ZUOK-DmhYb54l.js +1 -0
  24. package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-DmhYb54l.js +1 -0
  25. package/gui/dist/assets/{cose-bilkent-S5V4N54A-Dw2zOlxr.js → cose-bilkent-S5V4N54A-BDUCVkTl.js} +1 -1
  26. package/gui/dist/assets/{dagre-BM42HDAG-ebVZNyxI.js → dagre-BM42HDAG-DkoYBHXK.js} +1 -1
  27. package/gui/dist/assets/{diagram-2AECGRRQ-Bh5FUKy0.js → diagram-2AECGRRQ-BT1YJHon.js} +1 -1
  28. package/gui/dist/assets/{diagram-5GNKFQAL-YJJTJy-Q.js → diagram-5GNKFQAL-C00r6F35.js} +1 -1
  29. package/gui/dist/assets/{diagram-KO2AKTUF-D161kBvB.js → diagram-KO2AKTUF-B4wcN5a_.js} +1 -1
  30. package/gui/dist/assets/{diagram-LMA3HP47-Drcmx9J8.js → diagram-LMA3HP47-B9FqCfel.js} +1 -1
  31. package/gui/dist/assets/{diagram-OG6HWLK6-DRrxnW_H.js → diagram-OG6HWLK6-COPCDzdG.js} +1 -1
  32. package/gui/dist/assets/{erDiagram-TEJ5UH35-FrH4iEa3.js → erDiagram-TEJ5UH35-BGQGT2wN.js} +1 -1
  33. package/gui/dist/assets/{flowDiagram-I6XJVG4X-BMxSElvH.js → flowDiagram-I6XJVG4X-A7jKWUpi.js} +1 -1
  34. package/gui/dist/assets/{ganttDiagram-6RSMTGT7-H09FGGUr.js → ganttDiagram-6RSMTGT7-NhFkpY-x.js} +1 -1
  35. package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-DLhPfXNp.js → gitGraphDiagram-PVQCEYII-z0AqyYG4.js} +1 -1
  36. package/gui/dist/assets/{index-CdGsMXYw.js → index-Dub5OLKk.js} +95 -95
  37. package/gui/dist/assets/index-QkCDSQ2A.css +1 -0
  38. package/gui/dist/assets/{infoDiagram-5YYISTIA-CV8ctX8t.js → infoDiagram-5YYISTIA-D69UR9u9.js} +1 -1
  39. package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-BeuHUSPO.js → ishikawaDiagram-YF4QCWOH-DSTqIoro.js} +1 -1
  40. package/gui/dist/assets/{journeyDiagram-JHISSGLW-CfAHwYXQ.js → journeyDiagram-JHISSGLW-CekXdcoE.js} +1 -1
  41. package/gui/dist/assets/{kanban-definition-UN3LZRKU-CFF4KWbn.js → kanban-definition-UN3LZRKU-CseGvFK9.js} +1 -1
  42. package/gui/dist/assets/{linear-DwZpGeRp.js → linear-BtVl_9_C.js} +1 -1
  43. package/gui/dist/assets/{mindmap-definition-RKZ34NQL-BW0yW4ap.js → mindmap-definition-RKZ34NQL-XLjQIU37.js} +1 -1
  44. package/gui/dist/assets/{pieDiagram-4H26LBE5-plBCN_c7.js → pieDiagram-4H26LBE5-BcvUzWoW.js} +1 -1
  45. package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-Dmp9sOq_.js → quadrantDiagram-W4KKPZXB-BzoYEmNW.js} +1 -1
  46. package/gui/dist/assets/{requirementDiagram-4Y6WPE33-CthKC15J.js → requirementDiagram-4Y6WPE33-icKC1_Nd.js} +1 -1
  47. package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-BCAzzfPU.js → sankeyDiagram-5OEKKPKP-mi3i-2dZ.js} +1 -1
  48. package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-DgwOe82k.js → sequenceDiagram-3UESZ5HK-XrcDkRlM.js} +1 -1
  49. package/gui/dist/assets/{stateDiagram-AJRCARHV-B6k-x1TC.js → stateDiagram-AJRCARHV-UXaqrCNT.js} +1 -1
  50. package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-Bhdu4lv4.js +1 -0
  51. package/gui/dist/assets/{timeline-definition-PNZ67QCA-CqwqJkZw.js → timeline-definition-PNZ67QCA-yd6607p6.js} +1 -1
  52. package/gui/dist/assets/{vennDiagram-CIIHVFJN-REFDrI5t.js → vennDiagram-CIIHVFJN-BsRqUcIp.js} +1 -1
  53. package/gui/dist/assets/{wardley-L42UT6IY-53gLZWfR.js → wardley-L42UT6IY-BmdA7yk0.js} +1 -1
  54. package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-OPfKpNx-.js → wardleyDiagram-YWT4CUSO-CRdmckD8.js} +1 -1
  55. package/gui/dist/assets/{xychartDiagram-2RQKCTM6-HWGySgFg.js → xychartDiagram-2RQKCTM6-DWpNIQY-.js} +1 -1
  56. package/gui/dist/index.html +2 -2
  57. package/package.json +65 -65
  58. package/gui/dist/assets/channel-JejxY436.js +0 -1
  59. package/gui/dist/assets/classDiagram-4FO5ZUOK-CdAqI_QA.js +0 -1
  60. package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-CdAqI_QA.js +0 -1
  61. package/gui/dist/assets/index-DAzSbGj_.css +0 -1
  62. 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
- const status = await this.git.status();
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 changes block a safe merge — never risk clobbering them.
212
- // Surface as diverged so the user commits/pushes (or stashes) first.
213
- if (status.files.length > 0) {
214
- return { status: "diverged", ahead: status.ahead, behind: status.behind };
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
- // .gitignore (written by ensureRepo) excludes all derived/local state,
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 pull_docs to integrate them, then push again.");
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
- lines.push(`Hunk ${hunk.index} — section "${hunk.section}" (line ${hunk.line})`);
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
- const result = applyResolutions(original, operation, decisions);
116
- // The invariant is checked against the text itself rather than the hunk
117
- // count: staging a file that still holds markers tells git the conflict is
118
- // settled and lets the markers be committed as documentation content.
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}".`];
@@ -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 `Pulled ${res.changed.length} changed file(s):\n - ${res.changed.join("\n - ")}`;
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 `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.`;
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 divergence/conflict — surface it so the user can resolve first.
96
- if (pull.status === "diverged" || pull.status === "conflict") {
97
- return ok(`Pull step:\n${describePull(pull)}${reindexNote}\n\nPush skipped until the above is resolved.`);
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}`);
@@ -1 +1 @@
1
- export declare const APP_VERSION = "1.0.72";
1
+ export declare const APP_VERSION = "1.0.74";
@@ -1,2 +1,2 @@
1
1
  // Auto-generated version file - do not edit manually
2
- export const APP_VERSION = '1.0.72';
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-CdGsMXYw.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};
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};