@imenam/mcp-documentor 1.0.71 → 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 (72) hide show
  1. package/dist/src/core/conflict-inspector.d.ts +63 -0
  2. package/dist/src/core/conflict-inspector.js +157 -0
  3. package/dist/src/core/git-sync.d.ts +113 -8
  4. package/dist/src/core/git-sync.js +296 -45
  5. package/dist/src/core/merge-drivers/merge-config.d.ts +1 -0
  6. package/dist/src/core/merge-drivers/merge-config.js +68 -0
  7. package/dist/src/core/merge-drivers/merge-doc.d.ts +1 -0
  8. package/dist/src/core/merge-drivers/merge-doc.js +105 -0
  9. package/dist/src/core/merge-drivers/text-merge.d.ts +15 -0
  10. package/dist/src/core/merge-drivers/text-merge.js +53 -0
  11. package/dist/src/core/merge-drivers/three-way.d.ts +34 -0
  12. package/dist/src/core/merge-drivers/three-way.js +86 -0
  13. package/dist/src/gui-worker.js +21 -0
  14. package/dist/src/index.js +146 -12
  15. package/dist/src/tools/conflicts.d.ts +12 -0
  16. package/dist/src/tools/conflicts.js +190 -0
  17. package/dist/src/tools/sync.js +55 -11
  18. package/dist/src/version.d.ts +1 -1
  19. package/dist/src/version.js +1 -1
  20. package/gui/dist/assets/{arc-Carsq0k3.js → arc-BNuZ3dSL.js} +1 -1
  21. package/gui/dist/assets/{architectureDiagram-3BPJPVTR-caqcwdTw.js → architectureDiagram-3BPJPVTR-D8g3tPQx.js} +1 -1
  22. package/gui/dist/assets/{blockDiagram-GPEHLZMM-0wUO8k2I.js → blockDiagram-GPEHLZMM-limhup8_.js} +1 -1
  23. package/gui/dist/assets/{c4Diagram-AAUBKEIU-DS6q7fMy.js → c4Diagram-AAUBKEIU-Ct39JMIy.js} +1 -1
  24. package/gui/dist/assets/channel-uoEIgtym.js +1 -0
  25. package/gui/dist/assets/{chunk-2J33WTMH-CrJc-G6D.js → chunk-2J33WTMH-BkLJTezj.js} +1 -1
  26. package/gui/dist/assets/{chunk-4BX2VUAB-C5vtPFbB.js → chunk-4BX2VUAB-CR5NbhDR.js} +1 -1
  27. package/gui/dist/assets/{chunk-55IACEB6-BchyQUFM.js → chunk-55IACEB6-CtQYMnJS.js} +1 -1
  28. package/gui/dist/assets/{chunk-727SXJPM-BCIaWtKq.js → chunk-727SXJPM-B2q7tUn8.js} +1 -1
  29. package/gui/dist/assets/{chunk-AQP2D5EJ-BEQJDJt5.js → chunk-AQP2D5EJ-Bbcnrfrp.js} +1 -1
  30. package/gui/dist/assets/{chunk-FMBD7UC4-IfQ3QtrC.js → chunk-FMBD7UC4-D8gd4zRe.js} +1 -1
  31. package/gui/dist/assets/{chunk-ND2GUHAM-4Du33w7t.js → chunk-ND2GUHAM-DiZKzZtG.js} +1 -1
  32. package/gui/dist/assets/{chunk-QZHKN3VN-HR0CP6fa.js → chunk-QZHKN3VN-CrKx-e2_.js} +1 -1
  33. package/gui/dist/assets/classDiagram-4FO5ZUOK-DmhYb54l.js +1 -0
  34. package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-DmhYb54l.js +1 -0
  35. package/gui/dist/assets/{cose-bilkent-S5V4N54A-C9Re3ENx.js → cose-bilkent-S5V4N54A-BDUCVkTl.js} +1 -1
  36. package/gui/dist/assets/{dagre-BM42HDAG-DZmcvMMp.js → dagre-BM42HDAG-DkoYBHXK.js} +1 -1
  37. package/gui/dist/assets/{diagram-2AECGRRQ-EyJrAGht.js → diagram-2AECGRRQ-BT1YJHon.js} +1 -1
  38. package/gui/dist/assets/{diagram-5GNKFQAL-CHd3T9_b.js → diagram-5GNKFQAL-C00r6F35.js} +1 -1
  39. package/gui/dist/assets/{diagram-KO2AKTUF-CIyFrZSR.js → diagram-KO2AKTUF-B4wcN5a_.js} +1 -1
  40. package/gui/dist/assets/{diagram-LMA3HP47-BlkJALyz.js → diagram-LMA3HP47-B9FqCfel.js} +1 -1
  41. package/gui/dist/assets/{diagram-OG6HWLK6-P8LA7IZE.js → diagram-OG6HWLK6-COPCDzdG.js} +1 -1
  42. package/gui/dist/assets/{erDiagram-TEJ5UH35-BPywgWN1.js → erDiagram-TEJ5UH35-BGQGT2wN.js} +1 -1
  43. package/gui/dist/assets/{flowDiagram-I6XJVG4X-ByL0kGiV.js → flowDiagram-I6XJVG4X-A7jKWUpi.js} +1 -1
  44. package/gui/dist/assets/{ganttDiagram-6RSMTGT7-_pnaYCU4.js → ganttDiagram-6RSMTGT7-NhFkpY-x.js} +1 -1
  45. package/gui/dist/assets/{gitGraphDiagram-PVQCEYII-DN67CDGs.js → gitGraphDiagram-PVQCEYII-z0AqyYG4.js} +1 -1
  46. package/gui/dist/assets/{index-Bjc5MOe1.js → index-Dub5OLKk.js} +98 -98
  47. package/gui/dist/assets/index-QkCDSQ2A.css +1 -0
  48. package/gui/dist/assets/{infoDiagram-5YYISTIA-Bm5LEpzH.js → infoDiagram-5YYISTIA-D69UR9u9.js} +1 -1
  49. package/gui/dist/assets/{ishikawaDiagram-YF4QCWOH-DUYSULkT.js → ishikawaDiagram-YF4QCWOH-DSTqIoro.js} +1 -1
  50. package/gui/dist/assets/{journeyDiagram-JHISSGLW-BvJ1_f0L.js → journeyDiagram-JHISSGLW-CekXdcoE.js} +1 -1
  51. package/gui/dist/assets/{kanban-definition-UN3LZRKU-DDYKg6RY.js → kanban-definition-UN3LZRKU-CseGvFK9.js} +1 -1
  52. package/gui/dist/assets/{linear-HlpG9u6P.js → linear-BtVl_9_C.js} +1 -1
  53. package/gui/dist/assets/{mindmap-definition-RKZ34NQL-CPa_wo_s.js → mindmap-definition-RKZ34NQL-XLjQIU37.js} +1 -1
  54. package/gui/dist/assets/{pieDiagram-4H26LBE5-BWo0c4DP.js → pieDiagram-4H26LBE5-BcvUzWoW.js} +1 -1
  55. package/gui/dist/assets/{quadrantDiagram-W4KKPZXB-CLahU-6B.js → quadrantDiagram-W4KKPZXB-BzoYEmNW.js} +1 -1
  56. package/gui/dist/assets/{requirementDiagram-4Y6WPE33-BoYbgzQ7.js → requirementDiagram-4Y6WPE33-icKC1_Nd.js} +1 -1
  57. package/gui/dist/assets/{sankeyDiagram-5OEKKPKP-CGm79aMo.js → sankeyDiagram-5OEKKPKP-mi3i-2dZ.js} +1 -1
  58. package/gui/dist/assets/{sequenceDiagram-3UESZ5HK-BkknTD52.js → sequenceDiagram-3UESZ5HK-XrcDkRlM.js} +1 -1
  59. package/gui/dist/assets/{stateDiagram-AJRCARHV-DSBurgwC.js → stateDiagram-AJRCARHV-UXaqrCNT.js} +1 -1
  60. package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-Bhdu4lv4.js +1 -0
  61. package/gui/dist/assets/{timeline-definition-PNZ67QCA-CBHyrGuz.js → timeline-definition-PNZ67QCA-yd6607p6.js} +1 -1
  62. package/gui/dist/assets/{vennDiagram-CIIHVFJN-Dn9fr_bR.js → vennDiagram-CIIHVFJN-BsRqUcIp.js} +1 -1
  63. package/gui/dist/assets/{wardley-L42UT6IY-nMuySeAb.js → wardley-L42UT6IY-BmdA7yk0.js} +1 -1
  64. package/gui/dist/assets/{wardleyDiagram-YWT4CUSO-Byn2STLI.js → wardleyDiagram-YWT4CUSO-CRdmckD8.js} +1 -1
  65. package/gui/dist/assets/{xychartDiagram-2RQKCTM6-Bi-Y9wp5.js → xychartDiagram-2RQKCTM6-DWpNIQY-.js} +1 -1
  66. package/gui/dist/index.html +2 -2
  67. package/package.json +65 -65
  68. package/gui/dist/assets/channel-D51HexOc.js +0 -1
  69. package/gui/dist/assets/classDiagram-4FO5ZUOK-CU0dQ3zy.js +0 -1
  70. package/gui/dist/assets/classDiagram-v2-Q7XG4LA2-CU0dQ3zy.js +0 -1
  71. package/gui/dist/assets/index-hjkEDRmc.css +0 -1
  72. package/gui/dist/assets/stateDiagram-v2-BHNVJYJU-DfKn22XF.js +0 -1
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Reads and rewrites git conflict markers so the agent can resolve a document
3
+ * hunk by hunk without ever loading the document itself.
4
+ *
5
+ * Sides are named "local" and "remote" throughout, never git's ours/theirs:
6
+ * those two swap meaning between a merge and a rebase, and the whole point of
7
+ * this layer is that the caller never has to know which one is running.
8
+ */
9
+ /** Direct marker check, independent of hunk parsing - the invariant before staging. */
10
+ export declare function hasConflictMarkers(text: string): boolean;
11
+ export type HunkSide = "local" | "remote";
12
+ export interface ConflictHunk {
13
+ /** 1-based index within the file; the id the caller resolves against. */
14
+ index: number;
15
+ /** Nearest markdown heading above the hunk, for locating it without reading the doc. */
16
+ section: string;
17
+ /** 1-based line where the hunk's opening marker sits. */
18
+ line: number;
19
+ local: string[];
20
+ remote: string[];
21
+ /** Common ancestor, present only when the markers were written in diff3 style. */
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;
36
+ }
37
+ export interface ParsedFile {
38
+ lines: string[];
39
+ hunks: ConflictHunk[];
40
+ }
41
+ /** Splits a conflicted file into hunks. Handles both diff3 and plain markers. */
42
+ export declare function parseConflicts(text: string, operation: "merge" | "rebase" | "none"): ParsedFile;
43
+ export type Decision = {
44
+ index: number;
45
+ take: "local" | "remote" | "both" | "none";
46
+ } | {
47
+ index: number;
48
+ take: "custom";
49
+ content: string;
50
+ };
51
+ /**
52
+ * Rewrites the file with the chosen resolution for each listed hunk. Hunks with
53
+ * no decision keep their markers, so a partial resolution is safe: the file
54
+ * stays conflicted until every hunk has been decided.
55
+ */
56
+ export declare function applyResolutions(text: string, operation: "merge" | "rebase" | "none", decisions: Decision[]): {
57
+ text: string;
58
+ applied: number[];
59
+ unknown: number[];
60
+ remaining: number;
61
+ };
62
+ /** Truncates a side to a character budget, saying how much was withheld. */
63
+ export declare function preview(side: string[], maxChars: number): string;
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Reads and rewrites git conflict markers so the agent can resolve a document
3
+ * hunk by hunk without ever loading the document itself.
4
+ *
5
+ * Sides are named "local" and "remote" throughout, never git's ours/theirs:
6
+ * those two swap meaning between a merge and a rebase, and the whole point of
7
+ * this layer is that the caller never has to know which one is running.
8
+ */
9
+ const START = /^<{7}(?: (.*))?$/;
10
+ const BASE = /^\|{7}(?: (.*))?$/;
11
+ const SEPARATOR = /^={7}$/;
12
+ const END = /^>{7}(?: (.*))?$/;
13
+ const HEADING = /^(#{1,6})\s+(.*)$/;
14
+ /**
15
+ * git checks documents out with the platform's line endings, so a working-tree
16
+ * file on Windows is CRLF even though the driver wrote LF. Splitting on "\n"
17
+ * alone would leave a trailing CR on every line and no marker would ever match.
18
+ */
19
+ function splitLines(text) {
20
+ return text.split(/\r?\n/);
21
+ }
22
+ function lineEnding(text) {
23
+ return /\r\n/.test(text) ? "\r\n" : "\n";
24
+ }
25
+ /** Direct marker check, independent of hunk parsing - the invariant before staging. */
26
+ export function hasConflictMarkers(text) {
27
+ return splitLines(text).some((line) => START.test(line) || SEPARATOR.test(line) || END.test(line));
28
+ }
29
+ /**
30
+ * Maps a marker block onto absolute sides. In the file, the first block is
31
+ * always git's "ours": the branch being merged into for a merge, but the
32
+ * upstream being replayed onto for a rebase.
33
+ */
34
+ function sidesFor(operation, first, second) {
35
+ return operation === "rebase" ? { local: second, remote: first } : { local: first, remote: second };
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
+ }
50
+ function sectionFor(lines, hunkStart) {
51
+ for (let i = hunkStart - 1; i >= 0; i--) {
52
+ const match = lines[i].match(HEADING);
53
+ if (match)
54
+ return match[2].trim();
55
+ }
56
+ return "(document root)";
57
+ }
58
+ /** Splits a conflicted file into hunks. Handles both diff3 and plain markers. */
59
+ export function parseConflicts(text, operation) {
60
+ const lines = splitLines(text);
61
+ const frontmatter = frontmatterRange(lines);
62
+ const hunks = [];
63
+ for (let i = 0; i < lines.length; i++) {
64
+ if (!START.test(lines[i]))
65
+ continue;
66
+ const first = [];
67
+ const base = [];
68
+ const second = [];
69
+ let target = first;
70
+ let sawBase = false;
71
+ let closed = false;
72
+ let j = i + 1;
73
+ const label = lines[i].match(START)?.[1] ?? "";
74
+ for (; j < lines.length; j++) {
75
+ const line = lines[j];
76
+ if (BASE.test(line)) {
77
+ sawBase = true;
78
+ target = base;
79
+ }
80
+ else if (SEPARATOR.test(line)) {
81
+ target = second;
82
+ }
83
+ else if (END.test(line)) {
84
+ closed = true;
85
+ break;
86
+ }
87
+ else {
88
+ target.push(line);
89
+ }
90
+ }
91
+ // An unterminated marker is malformed input, not a hunk — leave it alone.
92
+ if (!closed)
93
+ continue;
94
+ const { local, remote } = sidesFor(operation, first, second);
95
+ hunks.push({
96
+ index: hunks.length + 1,
97
+ section: sectionFor(lines, i),
98
+ line: i + 1,
99
+ local,
100
+ remote,
101
+ ...(sawBase ? { base } : {}),
102
+ inFrontmatter: label.includes("frontmatter") || (frontmatter !== null && i >= frontmatter.start && i <= frontmatter.end),
103
+ hoisted: label.includes("frontmatter"),
104
+ });
105
+ i = j;
106
+ }
107
+ return { lines, hunks };
108
+ }
109
+ /**
110
+ * Rewrites the file with the chosen resolution for each listed hunk. Hunks with
111
+ * no decision keep their markers, so a partial resolution is safe: the file
112
+ * stays conflicted until every hunk has been decided.
113
+ */
114
+ export function applyResolutions(text, operation, decisions) {
115
+ const { lines, hunks } = parseConflicts(text, operation);
116
+ const byIndex = new Map(decisions.map((d) => [d.index, d]));
117
+ const unknown = decisions.map((d) => d.index).filter((i) => !hunks.some((h) => h.index === i));
118
+ const applied = [];
119
+ const out = [];
120
+ let cursor = 0;
121
+ for (const hunk of hunks) {
122
+ const start = hunk.line - 1;
123
+ // Re-find the closing marker: hunk bodies are stored without their markers.
124
+ let end = start;
125
+ while (end < lines.length && !END.test(lines[end]))
126
+ end++;
127
+ out.push(...lines.slice(cursor, start));
128
+ const decision = byIndex.get(hunk.index);
129
+ if (!decision) {
130
+ out.push(...lines.slice(start, end + 1));
131
+ }
132
+ else {
133
+ applied.push(hunk.index);
134
+ if (decision.take === "local")
135
+ out.push(...hunk.local);
136
+ else if (decision.take === "remote")
137
+ out.push(...hunk.remote);
138
+ else if (decision.take === "both")
139
+ out.push(...hunk.local, ...hunk.remote);
140
+ else if (decision.take === "custom")
141
+ out.push(...splitLines(decision.content));
142
+ // "none" drops the hunk entirely.
143
+ }
144
+ cursor = end + 1;
145
+ }
146
+ out.push(...lines.slice(cursor));
147
+ return { text: out.join(lineEnding(text)), applied, unknown, remaining: hunks.length - applied.length };
148
+ }
149
+ /** Truncates a side to a character budget, saying how much was withheld. */
150
+ export function preview(side, maxChars) {
151
+ const text = side.join("\n");
152
+ if (text.length <= maxChars)
153
+ return text;
154
+ const kept = text.slice(0, maxChars);
155
+ const hiddenLines = text.slice(maxChars).split("\n").length;
156
+ return `${kept}\n… [${hiddenLines} more line(s), ${text.length - maxChars} chars withheld]`;
157
+ }
@@ -1,6 +1,39 @@
1
1
  import type { StorageEngine } from "./storage-engine.js";
2
+ /**
3
+ * A merge and a rebase both leave conflicts in the index, but they invert the
4
+ * meaning of "ours" and "theirs" - so every side-picking decision has to know
5
+ * which one is running.
6
+ */
7
+ export type SyncOperation = "merge" | "rebase" | "none";
8
+ /** Which side of a conflict to keep, in terms that stay true under both operations. */
9
+ export type ConflictSide = "local" | "remote";
10
+ /** Who last touched a conflicted file on one side, for reporting without reading it. */
11
+ export interface ConflictAuthor {
12
+ author: string;
13
+ date: string;
14
+ /** Commit subject; push() puts the machine name in it. */
15
+ subject: string;
16
+ }
17
+ export interface ConflictedFile {
18
+ /** Path relative to the data directory. */
19
+ path: string;
20
+ kind: "content" | "delete-modify" | "add-add";
21
+ /** For delete-modify: the side that deleted the file. */
22
+ deletedOn?: ConflictSide;
23
+ local?: ConflictAuthor;
24
+ remote?: ConflictAuthor;
25
+ }
26
+ export interface ContinueResult {
27
+ operation: SyncOperation;
28
+ done: boolean;
29
+ /** Files still conflicted - a rebase stops once per replayed commit. */
30
+ remaining: string[];
31
+ /** Set only when the operation neither completed nor left a conflict to explain why. */
32
+ error?: string;
33
+ }
2
34
  export interface SyncStatus {
3
35
  branch: string;
36
+ operation: SyncOperation;
4
37
  ahead: number;
5
38
  behind: number;
6
39
  dirty: string[];
@@ -11,13 +44,11 @@ export type PullResult = {
11
44
  } | {
12
45
  status: "updated";
13
46
  changed: string[];
14
- } | {
15
- status: "diverged";
16
- ahead: number;
17
- behind: number;
47
+ autoCommitted?: boolean;
18
48
  } | {
19
49
  status: "conflict";
20
50
  conflicted: string[];
51
+ autoCommitted?: boolean;
21
52
  };
22
53
  export declare class SyncEngine {
23
54
  private storage;
@@ -31,6 +62,13 @@ export declare class SyncEngine {
31
62
  */
32
63
  private authedUrl;
33
64
  private requireConfig;
65
+ /**
66
+ * Registers the structured merge drivers in .git/config, which git never
67
+ * versions - so this runs on every machine, not just the one that created the
68
+ * repo. Values are absolute paths to the shipped scripts; git invokes them
69
+ * through a shell, hence the quoting and the forward slashes on Windows.
70
+ */
71
+ private ensureMergeDrivers;
34
72
  /**
35
73
  * Ensures the data dir is a git repo with a .gitignore and an authenticated
36
74
  * origin. The token is stored only in .git/config (which git never commits),
@@ -40,17 +78,84 @@ export declare class SyncEngine {
40
78
  ensureRepo(): Promise<void>;
41
79
  status(): Promise<SyncStatus>;
42
80
  /**
43
- * Defensive pull: only fast-forwards a clean working tree. Never auto-merges
44
- * a divergent history, so local data is never silently overwritten.
81
+ * Defensive pull. A clean tree that is purely behind fast-forwards; a
82
+ * divergent one is rebased, so local commits replay on top of the remote and
83
+ * the history stays linear - which is what makes per-doc `git log` attribution
84
+ * readable when reporting who changed what. An uncommitted local change is
85
+ * never risked: the pull reports the divergence and applies nothing.
45
86
  */
46
87
  pull(): Promise<PullResult>;
47
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;
48
98
  /** Stages versioned files, commits if there is anything to commit, and pushes. */
49
99
  push(message?: string): Promise<{
50
100
  pushed: boolean;
51
101
  committed: boolean;
52
102
  message: string;
53
103
  }>;
54
- /** Bulk-resolve conflicts by taking one side, then stage the resolved files. */
55
- resolveConflict(strategy: "mine" | "theirs", paths?: string[]): Promise<string[]>;
104
+ /**
105
+ * Detects whether a merge or a rebase is mid-flight. Read through
106
+ * `rev-parse --git-path` rather than assuming `.git` is a directory, so this
107
+ * still works in a worktree or a submodule.
108
+ */
109
+ operationInProgress(): Promise<SyncOperation>;
110
+ /** Index stages recorded for a conflicted path: 1 = base, 2 = ours, 3 = theirs. */
111
+ private conflictStages;
112
+ /**
113
+ * Finishes the in-flight merge or rebase once every conflict is staged.
114
+ * A rebase can stop again on the next replayed commit, so the caller is told
115
+ * what is still outstanding rather than being promised completion.
116
+ *
117
+ * `git rebase --continue` insists on an editor for the replayed commit
118
+ * message, and simple-git blocks both `-c core.editor` and GIT_EDITOR as
119
+ * injection vectors. This one command therefore goes out directly with a no-op
120
+ * editor - the value is a fixed literal here, never user input.
121
+ */
122
+ continueOperation(): Promise<ContinueResult>;
123
+ /**
124
+ * Runs one git command outside simple-git, which rejects a handful of config
125
+ * keys and environment variables as injection vectors - merge.*.driver and
126
+ * GIT_EDITOR among them. Everything passed here is a fixed literal or a path
127
+ * derived from the install location, never agent or user input.
128
+ */
129
+ private gitDirect;
130
+ /**
131
+ * The refs holding each side during the operation in flight. A rebase replays
132
+ * our commits onto the remote, so HEAD is the *remote* there - the mapping
133
+ * that makes every side label absolute.
134
+ */
135
+ private sideRefs;
136
+ private lastCommitFor;
137
+ /**
138
+ * Every conflicted file with its shape and its two authors - enough for the
139
+ * agent to decide what to look at without opening a single document.
140
+ */
141
+ listConflicts(): Promise<{
142
+ operation: SyncOperation;
143
+ files: ConflictedFile[];
144
+ }>;
145
+ /** Reads a conflicted file from the working tree, refusing to escape the data dir. */
146
+ readWorkingFile(relativePath: string): string;
147
+ /** Writes a resolved file back and stages it when nothing is left conflicted in it. */
148
+ writeResolvedFile(relativePath: string, content: string, stage: boolean): Promise<void>;
149
+ /**
150
+ * Bulk-resolve conflicts by keeping one side, then stage the result.
151
+ *
152
+ * The git flag depends on the operation in flight: a merge replays the remote
153
+ * onto our branch (--ours = local), while a rebase replays our commits onto
154
+ * the remote (--ours = remote). Picking the flag from the strategy alone would
155
+ * silently keep the wrong side under rebase.
156
+ */
157
+ resolveConflict(side: ConflictSide, paths?: string[]): Promise<{
158
+ resolved: string[];
159
+ operation: SyncOperation;
160
+ }>;
56
161
  }