@gitkraken/conflict-tools 0.0.1-beta.4 → 0.0.1-beta.6

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.
@@ -5,6 +5,6 @@ import { Conflict } from "../types/conflict.js";
5
5
  declare function extractConflict(filePath: string, deps: {
6
6
  git: ConflictGitPort;
7
7
  signal?: AbortSignal;
8
- }): Promise<Conflict | null>;
8
+ }, reason?: string): Promise<Conflict | null>;
9
9
  //#endregion
10
10
  export { extractConflict };
@@ -1 +1 @@
1
- import{readFile as e}from"../git/port-dispatch.js";import{parseConflictMarkers as t}from"./parser.js";async function n(n,r){let i=await e(r.git,n,{signal:r.signal}),a=t(i);return a.length===0?null:{filePath:n,markers:a,type:`text`,rawContent:i}}export{n as extractConflict};
1
+ import{readFile as e}from"../git/port-dispatch.js";import{parseConflictMarkers as t}from"./parser.js";async function n(n,r,i){if(i===`deleted-by-them`||i===`deleted-by-us`){let t=i===`deleted-by-them`?`theirs`:`ours`,a=``;try{a=await e(r.git,n,{signal:r.signal})}catch{}return{filePath:n,markers:[],type:`delete-modify`,rawContent:a,deletedBy:t}}let a=await e(r.git,n,{signal:r.signal}),o=t(a);return o.length===0?null:{filePath:n,markers:o,type:`text`,rawContent:a}}export{n as extractConflict};
@@ -11,5 +11,7 @@ declare function diff(git: ConflictGitPort, from: string, to: string, opts?: Con
11
11
  declare function log(git: ConflictGitPort, opts?: LogOptions): Promise<string>;
12
12
  declare function writeFile(git: ConflictGitPort, path: string, content: string, opts?: OpOptions): Promise<void>;
13
13
  declare function stageFiles(git: ConflictGitPort, paths: string[], opts?: OpOptions): Promise<void>;
14
+ declare function checkoutFile(git: ConflictGitPort, path: string, side: 'ours' | 'theirs', opts?: OpOptions): Promise<void>;
15
+ declare function removeFile(git: ConflictGitPort, path: string, opts?: OpOptions): Promise<void>;
14
16
  //#endregion
15
- export { blame, diff, grep, log, mergeBase, readFile, showFile, stageFiles, unmergedEntries, writeFile };
17
+ export { blame, checkoutFile, diff, grep, log, mergeBase, readFile, removeFile, showFile, stageFiles, unmergedEntries, writeFile };
@@ -1,2 +1,2 @@
1
1
  import{ConflictGitPortMissingOpError as e}from"../ports/git.js";import{sliceLines as t,truncateLines as n,truncateMatches as r}from"./truncation.js";function i(e){return`Search capped at ${e} matches. There may be more — narrow with a more specific pattern or path.`}function a(t,n){throw new e(t,n)}function o(e,t){return e!=null&&t!=null?`startLine=${e}, endLine=${t}`:e==null?`endLine=${t}`:`startLine=${e}`}async function s(e,t,n){return e.readFile?e.readFile(t,n):a(`readFile`)}async function c(e,r,i,s){let c;if(e.showFile)c=await e.showFile(r,i,s);else if(e.exec){let n=t(await e.exec([`show`,`${r}:${i}`],{signal:s?.signal}),s?.startLine,s?.endLine);if(!n.ok)return n.reason===`invalid-range`?`[Invalid range: ${o(n.startLine,n.endLine)} (startLine must be <= endLine).]`:`[Range out of bounds: file has ${n.total} lines, requested ${o(n.startLine,n.endLine)}.]`;c=n.content}else return a(`showFile`);return n(c,1e3,`Output capped at 1000 lines. Use startLine/endLine to read other regions of the file.`)}async function l(e,t){if(e.unmergedEntries)return e.unmergedEntries(t);if(e.exec){let n=await e.exec([`status`,`--porcelain=v2`],{signal:t?.signal}),r=[];for(let e of n.split(`
2
- `)){if(!e.startsWith(`u `))continue;let t=e.split(` `),n=t[1]??``,i=t.slice(10).join(` `);r.push({path:i,reason:_(n)})}return r}return a(`unmergedEntries`)}async function u(e,t,n,r){return e.mergeBase?e.mergeBase(t,n,r):e.exec?(await e.exec([`merge-base`,`--`,t,n],{signal:r?.signal})).trim():a(`mergeBase`)}async function d(e,t,r){let i=r?.startLine,s=r?.endLine,c=Number.isFinite(i),l=Number.isFinite(s);if(c&&l&&i>s)return`[Invalid range: ${o(i,s)} (startLine must be <= endLine).]`;let u;if(e.blame)u=await e.blame(t,r);else if(e.exec){let n=[`blame`,`--porcelain`],a=c?Math.max(1,i):void 0,o=l?Math.max(1,s):void 0;a!=null&&o!=null?n.push(`-L`,`${a},${o}`):a==null?o!=null&&n.push(`-L`,`1,${o}`):n.push(`-L`,`${a},`),r?.ref&&n.push(r.ref),n.push(`--`,t),u=await e.exec(n,{signal:r?.signal})}else return a(`blame`);return n(u,1e3,`Output capped at 1000 lines. Use startLine/endLine to inspect other regions.`)}async function f(e,t,n){let o=n?.maxResults,s=o!=null&&Number.isFinite(o)?Math.min(Math.max(1,o),100):100,c;if(e.grep)c=await e.grep(t,n);else if(e.exec){let r=[`grep`,`-n`,`--`,t];n?.ref&&r.push(n.ref),c=await e.exec(r,{signal:n?.signal})}else return a(`grep`);return r(c,s,i(s))}async function p(e,t,n,r){if(e.diff)return e.diff(t,n,r);if(e.exec){let i=[`diff`,t,n];return r?.path&&i.push(`--`,r.path),e.exec(i,{signal:r?.signal})}return a(`diff`)}async function m(e,t){let r=t?.maxCount,i=r!=null&&Number.isFinite(r)?Math.min(Math.max(1,r),100):100,o;if(e.log)o=await e.log({...t,maxCount:i});else if(e.exec){let n=[`log`,`--oneline`,`-n`,String(i)];t?.ref&&n.push(t.ref),t?.path&&n.push(`--`,t.path),o=await e.exec(n,{signal:t?.signal})}else return a(`log`);return n(o,100,`Output capped at 100 commits. Use path to scope to a file or refine the ref range.`)}async function h(e,t,n,r){return e.writeFile?e.writeFile(t,n,r):a(`writeFile`)}async function g(e,t,n){if(e.stageFiles)return e.stageFiles(t,n);if(e.exec){await e.exec([`add`,`--`,...t],{signal:n?.signal});return}return a(`stageFiles`)}function _(e){switch(e){case`DD`:return`both-deleted`;case`AU`:return`added-by-us`;case`UD`:return`deleted-by-them`;case`UA`:return`added-by-them`;case`DU`:return`deleted-by-us`;case`AA`:return`both-added`;case`UU`:return`both-modified`;default:return e}}export{d as blame,p as diff,f as grep,m as log,u as mergeBase,s as readFile,c as showFile,g as stageFiles,l as unmergedEntries,h as writeFile};
2
+ `)){if(!e.startsWith(`u `))continue;let t=e.split(` `),n=t[1]??``,i=t.slice(10).join(` `);r.push({path:i,reason:y(n)})}return r}return a(`unmergedEntries`)}async function u(e,t,n,r){return e.mergeBase?e.mergeBase(t,n,r):e.exec?(await e.exec([`merge-base`,`--`,t,n],{signal:r?.signal})).trim():a(`mergeBase`)}async function d(e,t,r){let i=r?.startLine,s=r?.endLine,c=Number.isFinite(i),l=Number.isFinite(s);if(c&&l&&i>s)return`[Invalid range: ${o(i,s)} (startLine must be <= endLine).]`;let u;if(e.blame)u=await e.blame(t,r);else if(e.exec){let n=[`blame`,`--porcelain`],a=c?Math.max(1,i):void 0,o=l?Math.max(1,s):void 0;a!=null&&o!=null?n.push(`-L`,`${a},${o}`):a==null?o!=null&&n.push(`-L`,`1,${o}`):n.push(`-L`,`${a},`),r?.ref&&n.push(r.ref),n.push(`--`,t),u=await e.exec(n,{signal:r?.signal})}else return a(`blame`);return n(u,1e3,`Output capped at 1000 lines. Use startLine/endLine to inspect other regions.`)}async function f(e,t,n){let o=n?.maxResults,s=o!=null&&Number.isFinite(o)?Math.min(Math.max(1,o),100):100,c;if(e.grep)c=await e.grep(t,n);else if(e.exec){let r=[`grep`,`-n`,`--`,t];n?.ref&&r.push(n.ref),c=await e.exec(r,{signal:n?.signal})}else return a(`grep`);return r(c,s,i(s))}async function p(e,t,n,r){if(e.diff)return e.diff(t,n,r);if(e.exec){let i=[`diff`,t,n];return r?.path&&i.push(`--`,r.path),e.exec(i,{signal:r?.signal})}return a(`diff`)}async function m(e,t){let r=t?.maxCount,i=r!=null&&Number.isFinite(r)?Math.min(Math.max(1,r),100):100,o;if(e.log)o=await e.log({...t,maxCount:i});else if(e.exec){let n=[`log`,`--oneline`,`-n`,String(i)];t?.ref&&n.push(t.ref),t?.path&&n.push(`--`,t.path),o=await e.exec(n,{signal:t?.signal})}else return a(`log`);return n(o,100,`Output capped at 100 commits. Use path to scope to a file or refine the ref range.`)}async function h(e,t,n,r){return e.writeFile?e.writeFile(t,n,r):a(`writeFile`)}async function g(e,t,n){if(e.stageFiles)return e.stageFiles(t,n);if(e.exec){await e.exec([`add`,`--`,...t],{signal:n?.signal});return}return a(`stageFiles`)}async function _(e,t,n,r){if(e.checkoutFile)return e.checkoutFile(t,n,r);if(e.exec){await e.exec([`checkout`,`--${n}`,`--`,t],{signal:r?.signal});return}return a(`checkoutFile`)}async function v(e,t,n){if(e.removeFile)return e.removeFile(t,n);if(e.exec){await e.exec([`rm`,`--`,t],{signal:n?.signal});return}return a(`removeFile`)}function y(e){switch(e){case`DD`:return`both-deleted`;case`AU`:return`added-by-us`;case`UD`:return`deleted-by-them`;case`UA`:return`added-by-them`;case`DU`:return`deleted-by-us`;case`AA`:return`both-added`;case`UU`:return`both-modified`;default:return e}}export{d as blame,_ as checkoutFile,p as diff,f as grep,m as log,u as mergeBase,s as readFile,v as removeFile,c as showFile,g as stageFiles,l as unmergedEntries,h as writeFile};
package/dist/index.d.ts CHANGED
@@ -3,8 +3,10 @@ import { ConflictError, ConflictErrorCode } from "./errors/conflict.js";
3
3
  import { BlameOptions, ConflictDiffOptions, ConflictGitOps, ConflictGitPort, ConflictGitPortMissingOpError, GrepOptions, LogOptions, OpOptions, ShowFileOptions, UnmergedEntry } from "./ports/git.js";
4
4
  import { Conflict, ConflictMarker, ConflictType } from "./types/conflict.js";
5
5
  import { extractConflict } from "./extraction/extract.js";
6
+ import { checkoutFile, removeFile } from "./git/port-dispatch.js";
6
7
  import { ToolCall, ToolDefinition, ToolResult } from "./types/tool.js";
7
8
  import { ConflictModelMessage, ConflictModelParams, ConflictModelPort, ConflictModelResult, ResolverConfig } from "./ports/model.js";
9
+ import { ResolutionVerifier, VerificationResult, defaultVerifier } from "./ports/verify.js";
8
10
  import { Resolution, ResolutionContext, ResolutionMetrics, ResolutionRefs, ResolutionStrategy, ResolvedChunk, ThreeWayDiff } from "./types/resolution.js";
9
11
  import { createResolution } from "./resolution/create-resolution.js";
10
12
  import { takeOurs, takeTheirs } from "./resolution/take-strategy.js";
@@ -13,4 +15,4 @@ import { ConflictProgressEvent } from "./types/events.js";
13
15
  import { resolveConflict } from "./resolver/resolve-conflict.js";
14
16
  import { StepConfig, StepResult } from "./types/step.js";
15
17
  import { resolveConflicts } from "./resolver/resolve-conflicts.js";
16
- export { AIError, type AIErrorCode, type BlameOptions, type Conflict, type ConflictDiffOptions, ConflictError, type ConflictErrorCode, type ConflictGitOps, type ConflictGitPort, ConflictGitPortMissingOpError, type ConflictMarker, type ConflictModelMessage, type ConflictModelParams, type ConflictModelPort, type ConflictModelResult, type ConflictProgressEvent, type ConflictType, type GrepOptions, type LogOptions, type OpOptions, type Resolution, type ResolutionContext, type ResolutionMetrics, type ResolutionRefs, type ResolutionStrategy, type ResolvedChunk, type ResolverConfig, type ShowFileOptions, type StepConfig, type StepResult, type ThreeWayDiff, type ToolCall, type ToolDefinition, type ToolResult, type UnmergedEntry, applyResolutions, createResolution, extractConflict, resolveConflict, resolveConflicts, takeOurs, takeTheirs };
18
+ export { AIError, type AIErrorCode, type BlameOptions, type Conflict, type ConflictDiffOptions, ConflictError, type ConflictErrorCode, type ConflictGitOps, type ConflictGitPort, ConflictGitPortMissingOpError, type ConflictMarker, type ConflictModelMessage, type ConflictModelParams, type ConflictModelPort, type ConflictModelResult, type ConflictProgressEvent, type ConflictType, type GrepOptions, type LogOptions, type OpOptions, type Resolution, type ResolutionContext, type ResolutionMetrics, type ResolutionRefs, type ResolutionStrategy, type ResolutionVerifier, type ResolvedChunk, type ResolverConfig, type ShowFileOptions, type StepConfig, type StepResult, type ThreeWayDiff, type ToolCall, type ToolDefinition, type ToolResult, type UnmergedEntry, type VerificationResult, applyResolutions, checkoutFile, createResolution, defaultVerifier, extractConflict, removeFile, resolveConflict, resolveConflicts, takeOurs, takeTheirs };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{AIError as e}from"./errors/ai.js";import{ConflictError as t}from"./errors/conflict.js";import{ConflictGitPortMissingOpError as n}from"./ports/git.js";import{extractConflict as r}from"./extraction/extract.js";import{createResolution as i}from"./resolution/create-resolution.js";import{takeOurs as a,takeTheirs as o}from"./resolution/take-strategy.js";import{applyResolutions as s}from"./resolver/apply-resolutions.js";import{resolveConflict as c}from"./resolver/resolve-conflict.js";import{resolveConflicts as l}from"./resolver/resolve-conflicts.js";export{e as AIError,t as ConflictError,n as ConflictGitPortMissingOpError,s as applyResolutions,i as createResolution,r as extractConflict,c as resolveConflict,l as resolveConflicts,a as takeOurs,o as takeTheirs};
1
+ import{AIError as e}from"./errors/ai.js";import{ConflictError as t}from"./errors/conflict.js";import{ConflictGitPortMissingOpError as n}from"./ports/git.js";import{checkoutFile as r,removeFile as i}from"./git/port-dispatch.js";import{extractConflict as a}from"./extraction/extract.js";import{defaultVerifier as o}from"./ports/verify.js";import{createResolution as s}from"./resolution/create-resolution.js";import{takeOurs as c,takeTheirs as l}from"./resolution/take-strategy.js";import{applyResolutions as u}from"./resolver/apply-resolutions.js";import{resolveConflict as d}from"./resolver/resolve-conflict.js";import{resolveConflicts as f}from"./resolver/resolve-conflicts.js";export{e as AIError,t as ConflictError,n as ConflictGitPortMissingOpError,u as applyResolutions,r as checkoutFile,s as createResolution,o as defaultVerifier,a as extractConflict,i as removeFile,d as resolveConflict,f as resolveConflicts,c as takeOurs,l as takeTheirs};
@@ -62,6 +62,8 @@ interface ConflictGitOps {
62
62
  log?: (opts?: LogOptions) => Promise<string>;
63
63
  writeFile?: (path: string, content: string, opts?: OpOptions) => Promise<void>;
64
64
  stageFiles?: (paths: string[], opts?: OpOptions) => Promise<void>;
65
+ checkoutFile?: (path: string, side: 'ours' | 'theirs', opts?: OpOptions) => Promise<void>;
66
+ removeFile?: (path: string, opts?: OpOptions) => Promise<void>;
65
67
  }
66
68
  type ConflictGitPort = ConflictGitOps & {
67
69
  exec?: GitPort['exec'];
@@ -0,0 +1,11 @@
1
+ //#region src/ports/verify.d.ts
2
+ interface VerificationResult {
3
+ valid: boolean;
4
+ feedback?: string;
5
+ }
6
+ interface ResolutionVerifier {
7
+ verify(filePath: string, content: string): Promise<VerificationResult>;
8
+ }
9
+ declare const defaultVerifier: ResolutionVerifier;
10
+ //#endregion
11
+ export { ResolutionVerifier, VerificationResult, defaultVerifier };
@@ -0,0 +1,2 @@
1
+ const e=[/^<{7}(\s|$)/,/^\|{7}(\s|$)/,/^={7}(\s|$)/,/^>{7}(\s|$)/],t={async verify(t,n){let r=n.split(`
2
+ `),i=[];for(let t=0;t<r.length;t++){let n=r[t]??``;for(let r of e)if(r.test(n)){i.push(t+1);break}}return i.length===0?{valid:!0}:{valid:!1,feedback:`Residual conflict markers found at line(s) ${i.join(`, `)}.`}}};export{t as defaultVerifier};
@@ -6,6 +6,7 @@ interface CreateResolutionOptions {
6
6
  confidence: number;
7
7
  description?: string;
8
8
  metrics?: ResolutionMetrics;
9
+ fileStrategy?: 'ours' | 'theirs';
9
10
  }
10
11
  declare function createResolution(conflict: Conflict, chunks: ResolvedChunk[], options: CreateResolutionOptions): Resolution;
11
12
  //#endregion
@@ -1 +1 @@
1
- import{applyChunks as e}from"./chunk-apply.js";function t(t,i,a){let o=a.description!==void 0&&a.description.trim().length>0?a.description:r(i);return{filePath:t.filePath,content:e(t.rawContent,t.markers,i),strategy:n(i),confidence:a.confidence,description:o,chunks:i,...a.metrics?{metrics:a.metrics}:{}}}function n(e){if(e.length===0)return`skipped`;let t=new Set(e.map(e=>e.strategy));if(t.size===1){if(t.has(`ours`))return`take-ours`;if(t.has(`theirs`))return`take-theirs`}return`ai`}function r(e){if(e.length===0)return`No markers resolved.`;let t=new Set(e.map(e=>e.strategy));if(t.size===1){if(t.has(`ours`))return`All markers resolved by taking ours.`;if(t.has(`theirs`))return`All markers resolved by taking theirs.`}return`Resolved ${e.length} marker(s) with mixed strategies.`}export{t as createResolution};
1
+ import{applyChunks as e}from"./chunk-apply.js";function t(t,i,a){let o=i;a.fileStrategy&&(o=t.markers.map((e,t)=>({markerIndex:t,strategy:a.fileStrategy})));let s=a.description!==void 0&&a.description.trim().length>0?a.description:r(o),c=a.fileStrategy?a.fileStrategy===`ours`?`take-ours`:`take-theirs`:n(o);return{filePath:t.filePath,content:e(t.rawContent,t.markers,o),strategy:c,confidence:a.confidence,description:s,chunks:o,...a.metrics?{metrics:a.metrics}:{},...t.type===void 0?{}:{conflictType:t.type},...t.deletedBy===void 0?{}:{deletedBy:t.deletedBy}}}function n(e){if(e.length===0)return`skipped`;let t=new Set(e.map(e=>e.strategy));if(t.size===1){if(t.has(`ours`))return`take-ours`;if(t.has(`theirs`))return`take-theirs`}return`ai`}function r(e){if(e.length===0)return`No markers resolved.`;let t=new Set(e.map(e=>e.strategy));if(t.size===1){if(t.has(`ours`))return`All markers resolved by taking ours.`;if(t.has(`theirs`))return`All markers resolved by taking theirs.`}return`Resolved ${e.length} marker(s) with mixed strategies.`}export{t as createResolution};
@@ -1 +1 @@
1
- import{createResolution as e}from"./create-resolution.js";function t(t){return e(t,t.markers.map((e,t)=>({markerIndex:t,strategy:`ours`})),{confidence:1})}function n(t){return e(t,t.markers.map((e,t)=>({markerIndex:t,strategy:`theirs`})),{confidence:1})}export{t as takeOurs,n as takeTheirs};
1
+ import{createResolution as e}from"./create-resolution.js";function t(t){return e(t,[],{confidence:1,fileStrategy:`ours`})}function n(t){return e(t,[],{confidence:1,fileStrategy:`theirs`})}export{t as takeOurs,n as takeTheirs};
@@ -1 +1 @@
1
- import{stageFiles as e,writeFile as t}from"../git/port-dispatch.js";async function n(n,r){let i=n.filter(e=>e.strategy!==`skipped`);if(i.length!==0){for(let e of i)await t(r.git,e.filePath,e.content);await e(r.git,i.map(e=>e.filePath))}}export{n as applyResolutions};
1
+ import{checkoutFile as e,removeFile as t,stageFiles as n,writeFile as r}from"../git/port-dispatch.js";async function i(i,a){let o=i.filter(e=>e.strategy!==`skipped`);if(o.length===0)return;let s=new Set;for(let n of o)if(n.strategy===`take-ours`||n.strategy===`take-theirs`){let r=n.strategy===`take-ours`?`ours`:`theirs`;n.conflictType===`delete-modify`&&n.deletedBy===r?(await t(a.git,n.filePath),s.add(n.filePath)):await e(a.git,n.filePath,r)}else await r(a.git,n.filePath,n.content);let c=o.filter(e=>!s.has(e.filePath)).map(e=>e.filePath);c.length>0&&await n(a.git,c)}export{i as applyResolutions};
@@ -1,5 +1,6 @@
1
1
  import { ConflictGitPort } from "../ports/git.js";
2
2
  import { ConflictModelPort, ResolverConfig } from "../ports/model.js";
3
+ import { ResolutionVerifier } from "../ports/verify.js";
3
4
  import { ConflictProgressEvent } from "../types/events.js";
4
5
 
5
6
  //#region src/resolver/deps.d.ts
@@ -9,6 +10,7 @@ interface ResolverDeps<C extends ResolverConfig = ResolverConfig> {
9
10
  config?: C;
10
11
  onProgress?: (event: ConflictProgressEvent) => void;
11
12
  signal?: AbortSignal;
13
+ verifier?: ResolutionVerifier;
12
14
  }
13
15
  //#endregion
14
16
  export { ResolverDeps };
@@ -5,6 +5,7 @@ import { ResolverDeps } from "./deps.js";
5
5
  //#region src/resolver/loop.d.ts
6
6
  interface ResolverLoopResult {
7
7
  chunks: ResolvedChunk[];
8
+ strategy?: 'ours' | 'theirs';
8
9
  confidence: number;
9
10
  description: string;
10
11
  metrics: ResolutionMetrics;
@@ -1 +1 @@
1
- import{AIError as e}from"../errors/ai.js";import{parseModelResponse as t}from"./parse-response.js";import{buildPrompt as n}from"./prompt.js";import{createResolverTools as r}from"./tools.js";import{validateChunks as i}from"./validate.js";async function a(a,s,c){let l=c.config?.maxSteps??15,u=c.config?.temperature??0,{definitions:d,execute:f}=r(c.git),{system:p,userMessage:m}=n(a,s),h=[{role:`user`,content:m}],g=c.signal?{signal:c.signal}:void 0,_=0,v=0,y=[];for(;_<l;){_++;let e=await c.model.generate({system:p,messages:[...h],tools:d,temperature:u,signal:c.signal});if(e.usage&&(y.push(e.usage),c.onProgress?.({type:`resolver:step-usage`,filePath:a.filePath,stepNumber:_,usage:e.usage})),e.toolCalls&&e.toolCalls.length>0){h.push({role:`assistant`,content:e.text,toolCalls:e.toolCalls});for(let t of e.toolCalls){c.onProgress?.({type:`resolver:tool-call`,filePath:a.filePath,tool:t.name,args:t.args,stepNumber:_}),v++;let e=await f(t,g);h.push({role:`tool`,toolCallId:e.toolCallId,toolName:t.name,content:e.content})}continue}let n=e.text??``;if(!n.trim()){h.push({role:`assistant`,content:``}),h.push({role:`user`,content:`Your response was empty. Please respond with a valid JSON object containing "confidence" (0-1), "description" (1-2 sentences), and "chunks" (array of { markerIndex, strategy, content? }).`});continue}let r;try{r=t(n,a.markers.length)}catch(e){let t=e instanceof Error?e.message:String(e);h.push({role:`assistant`,content:n}),h.push({role:`user`,content:`Failed to parse your response: ${t}. Please respond with a valid JSON object containing "confidence" (0-1), "description" (1-2 sentences), and "chunks" (array of { markerIndex, strategy, content? }).`});continue}let s=i(r.chunks,a);if(!s.valid){h.push({role:`assistant`,content:n}),h.push({role:`user`,content:`Validation failed: ${s.feedback}\nPlease fix the issues and respond with a corrected JSON object.`});continue}return{chunks:r.chunks,confidence:r.confidence,description:r.description,metrics:o(y,_,v)}}throw new e(`VALIDATION_EXHAUSTED`,`Resolver exhausted ${l} steps without producing a valid resolution for ${a.filePath}.`)}function o(e,t,n){let r={inputTokens:0,outputTokens:0};for(let t of e)r.inputTokens+=t.inputTokens,r.outputTokens+=t.outputTokens,t.cacheReadTokens&&(r.cacheReadTokens=(r.cacheReadTokens??0)+t.cacheReadTokens),t.cacheWriteTokens&&(r.cacheWriteTokens=(r.cacheWriteTokens??0)+t.cacheWriteTokens),t.reasoningTokens&&(r.reasoningTokens=(r.reasoningTokens??0)+t.reasoningTokens),t.cost&&(r.cost=(r.cost??0)+t.cost);return{...r,stepCount:t,toolCallCount:n}}export{a as runResolverLoop};
1
+ import{AIError as e}from"../errors/ai.js";import{applyChunks as t}from"../resolution/chunk-apply.js";import{parseModelResponse as n}from"./parse-response.js";import{buildPrompt as r}from"./prompt.js";import{createResolverTools as i}from"./tools.js";import{validateChunks as a}from"./validate.js";async function o(o,c,l){let u=l.config?.maxSteps??15,d=l.config?.temperature??0,{definitions:f,execute:p}=i(l.git),{system:m,userMessage:h}=r(o,c,{maxSteps:u}),g=[{role:`user`,content:h}],_=l.signal?{signal:l.signal}:void 0,v=0,y=0,b=[];for(;v<u;){v++;let e=await l.model.generate({system:m,messages:[...g],tools:f,temperature:d,signal:l.signal});if(e.usage&&(b.push(e.usage),l.onProgress?.({type:`resolver:step-usage`,filePath:o.filePath,stepNumber:v,usage:e.usage})),e.toolCalls&&e.toolCalls.length>0){g.push({role:`assistant`,content:e.text,toolCalls:e.toolCalls});for(let t of e.toolCalls){l.onProgress?.({type:`resolver:tool-call`,filePath:o.filePath,tool:t.name,args:t.args,stepNumber:v}),y++;let e=await p(t,_);g.push({role:`tool`,toolCallId:e.toolCallId,toolName:t.name,content:e.content})}continue}let r=e.text??``;if(!r.trim()){g.push({role:`assistant`,content:``}),g.push({role:`user`,content:`Your response was empty. Please respond with a valid JSON object containing "confidence" (0-1), "description" (1-2 sentences), and either "strategy" ("ours" or "theirs") for a file-level decision, or "chunks" (array of { markerIndex, strategy, content? }).`});continue}let i;try{i=n(r,o.markers.length)}catch(e){let t=e instanceof Error?e.message:String(e);g.push({role:`assistant`,content:r}),g.push({role:`user`,content:`Failed to parse your response: ${t}. Please respond with a valid JSON object containing "confidence" (0-1), "description" (1-2 sentences), and either "strategy" ("ours" or "theirs") for a file-level decision, or "chunks" (array of { markerIndex, strategy, content? }).`});continue}if(i.strategy)return{chunks:i.chunks,strategy:i.strategy,confidence:i.confidence,description:i.description,metrics:s(b,v,y)};if(o.markers.length===0&&!i.strategy){g.push({role:`assistant`,content:r}),g.push({role:`user`,content:`Delete-modify conflicts require a file-level strategy field ("ours" or "theirs"). Please respond with a JSON object containing "strategy".`});continue}let c=a(i.chunks,o);if(!c.valid){g.push({role:`assistant`,content:r}),g.push({role:`user`,content:`Validation failed: ${c.feedback}\nPlease fix the issues and respond with a corrected JSON object. Make sure every marker index appears exactly once, or use a top-level "strategy" field.`});continue}if(l.verifier&&o.type===`text`){let e=t(o.rawContent,o.markers,i.chunks),n=await l.verifier.verify(o.filePath,e);if(!n.valid){g.push({role:`assistant`,content:r}),g.push({role:`user`,content:`Verification failed: ${n.feedback??`Content did not pass verification.`}\nPlease fix and respond with corrected JSON.`});continue}}return{chunks:i.chunks,confidence:i.confidence,description:i.description,metrics:s(b,v,y)}}throw new e(`VALIDATION_EXHAUSTED`,`Resolver exhausted ${u} steps without producing a valid resolution for ${o.filePath}.`)}function s(e,t,n){let r={inputTokens:0,outputTokens:0};for(let t of e)r.inputTokens+=t.inputTokens,r.outputTokens+=t.outputTokens,t.cacheReadTokens&&(r.cacheReadTokens=(r.cacheReadTokens??0)+t.cacheReadTokens),t.cacheWriteTokens&&(r.cacheWriteTokens=(r.cacheWriteTokens??0)+t.cacheWriteTokens),t.reasoningTokens&&(r.reasoningTokens=(r.reasoningTokens??0)+t.reasoningTokens),t.cost&&(r.cost=(r.cost??0)+t.cost);return{...r,stepCount:t,toolCallCount:n}}export{o as runResolverLoop};
@@ -5,6 +5,7 @@ interface ParsedResolution {
5
5
  confidence: number;
6
6
  description: string;
7
7
  chunks: ResolvedChunk[];
8
+ strategy?: 'ours' | 'theirs';
8
9
  }
9
10
  declare function parseModelResponse(text: string, markerCount: number): ParsedResolution;
10
11
  //#endregion
@@ -1 +1 @@
1
- function e(e,n){let r=t(e);if(r===null)throw Error(`No valid JSON object found in model response.`);let i;try{i=JSON.parse(r)}catch{throw Error(`Failed to parse JSON from model response.`)}if(typeof i!=`object`||!i||Array.isArray(i))throw Error(`Model response must be a JSON object.`);let a=i,o=a.confidence;if(typeof o!=`number`||!Number.isFinite(o)||o<0||o>1)throw Error(`Field "confidence" must be a number in [0, 1].`);let s=a.description;if(typeof s!=`string`||s.trim().length===0)throw Error(`Field "description" must be a non-empty string.`);let c=a.chunks;if(!Array.isArray(c))throw Error(`Field "chunks" must be an array.`);let l=[];for(let e of c){if(typeof e!=`object`||!e)throw Error(`Each chunk must be an object.`);let t=e,r=t.markerIndex,i=t.strategy;if(typeof r!=`number`||!Number.isInteger(r))throw Error(`Each chunk must have an integer markerIndex.`);if(r<0||r>=n)throw Error(`markerIndex ${r} is out of range (0..${n-1}).`);if(i===`ours`||i===`theirs`)l.push({markerIndex:r,strategy:i});else if(i===`merged`){let e=t.content;if(typeof e!=`string`)throw Error(`Chunk at markerIndex ${r} with strategy "merged" must have a string "content".`);l.push({markerIndex:r,strategy:i,content:e})}else throw Error(`Unknown strategy "${String(i)}" at markerIndex ${r}.`)}return{confidence:o,description:s.trim(),chunks:l}}function t(e){let t=e.trim(),n=/```(?:json)?\s*\n([\s\S]*?)\n\s*```/.exec(t);if(n){let e=n[1];if(e!==void 0)return e.trim()}let r=/\{[\s\S]*\}/.exec(t);return r?r[0]:null}export{e as parseModelResponse};
1
+ function e(e,n){let r=t(e);if(r===null)throw Error(`No valid JSON object found in model response.`);let i;try{i=JSON.parse(r)}catch{throw Error(`Failed to parse JSON from model response.`)}if(typeof i!=`object`||!i||Array.isArray(i))throw Error(`Model response must be a JSON object.`);let a=i,o=a.confidence;if(typeof o!=`number`||!Number.isFinite(o)||o<0||o>1)throw Error(`Field "confidence" must be a number in [0, 1].`);let s=a.description;if(typeof s!=`string`||s.trim().length===0)throw Error(`Field "description" must be a non-empty string.`);if(a.strategy!==void 0){let e=a.strategy;if(e!==`ours`&&e!==`theirs`)throw Error(`Field "strategy" must be "ours" or "theirs".`);return{confidence:o,description:s.trim(),chunks:[],strategy:e}}let c=a.chunks;if(!Array.isArray(c))throw Error(`Field "chunks" must be an array. Each chunk needs { markerIndex, strategy, content? }.`);let l=[];for(let e of c){if(typeof e!=`object`||!e)throw Error(`Each chunk must be an object.`);let t=e,r=t.markerIndex,i=t.strategy;if(typeof r!=`number`||!Number.isInteger(r))throw Error(`Each chunk must have an integer markerIndex.`);if(r<0||r>=n)throw Error(`markerIndex ${r} is out of range. Valid range: 0..${n-1}.`);if(i===`ours`||i===`theirs`)l.push({markerIndex:r,strategy:i});else if(i===`merged`){let e=t.content;if(typeof e!=`string`)throw Error(`Chunk at markerIndex ${r} with strategy "merged" must have a string "content".`);l.push({markerIndex:r,strategy:i,content:e})}else throw Error(`Unknown strategy "${String(i)}" at markerIndex ${r}. Valid strategies: "ours", "theirs", "merged".`)}return{confidence:o,description:s.trim(),chunks:l}}function t(e){let t=e.trim(),n=/```(?:json)?\s*\n([\s\S]*?)\n\s*```/.exec(t);if(n){let e=n[1];if(e!==void 0)return e.trim()}let r=/\{[\s\S]*\}/.exec(t);return r?r[0]:null}export{e as parseModelResponse};
@@ -6,6 +6,8 @@ interface PromptPair {
6
6
  system: string;
7
7
  userMessage: string;
8
8
  }
9
- declare function buildPrompt(conflict: Conflict, context: ResolutionContext): PromptPair;
9
+ declare function buildPrompt(conflict: Conflict, context: ResolutionContext, opts?: {
10
+ maxSteps?: number;
11
+ }): PromptPair;
10
12
  //#endregion
11
13
  export { PromptPair, buildPrompt };
@@ -1,4 +1,4 @@
1
- function e(e,r){return{system:t(e),userMessage:n(e,r)}}function t(e){let t=e.markers.length-1;return`You are a git conflict resolver. Your task is to resolve merge conflicts by analyzing the conflicting changes and producing a correct resolution for each conflict marker.
1
+ function e(e,r,i){return{system:t(e,i?.maxSteps),userMessage:n(e,r)}}function t(e,t){return`You are a git conflict resolver. Your task is to resolve merge conflicts by analyzing the conflicting changes and producing a correct resolution for each conflict marker.
2
2
 
3
3
  You will receive a file with conflict markers. Each marker has an "ours" side (current branch) and a "theirs" side (incoming branch), and optionally a "base" (common ancestor).
4
4
 
@@ -6,6 +6,8 @@ When provided, the "Three-way diffs" section shows what each side changed relati
6
6
 
7
7
  You have access to tools to inspect the repository: show_file_at_ref, grep, blame, diff, log. Use them to understand the intent behind each change before resolving.
8
8
 
9
+ You have a budget of ${t??15} tool-calling steps. If you have used more than half your budget without resolving, stop investigating and produce your best resolution now.
10
+
9
11
  Tool outputs are capped to keep context manageable. Prefer narrow queries: pass startLine/endLine when you need specific regions of a file, use path scope on grep/log, and use specific patterns over broad ones. If a tool response includes a 'capped' header, the underlying data is larger — re-run with a narrower range or filter to see other regions.
10
12
 
11
13
  When you are ready to resolve, respond with a JSON object in this format:
@@ -22,16 +24,43 @@ When you are ready to resolve, respond with a JSON object in this format:
22
24
  }
23
25
  \`\`\`
24
26
 
27
+ You may respond with a file-level strategy instead of per-chunk resolution:
28
+
29
+ \`\`\`json
30
+ {
31
+ "confidence": 0.95,
32
+ "description": "Taking ours — theirs has no meaningful changes.",
33
+ "strategy": "ours"
34
+ }
35
+ \`\`\`
36
+
37
+ Use file-level "ours" or "theirs" ONLY when one side has no meaningful changes relative to base (whitespace, formatting, or identical to base). If BOTH sides introduce meaningful changes, you MUST use per-chunk resolution with "chunks".
38
+
25
39
  Top-level fields:
26
40
  - "confidence": a number in [0, 1]. 1 = absolutely sure the resolution preserves both sides' intent. 0.5 = significant uncertainty. Lower if the conflict is ambiguous, semantics are unclear, or you had to guess.
41
+
42
+ Confidence scoring guide:
43
+ - 1.0: Trivial — only one side changed, or changes are in completely independent regions
44
+ - 0.8–0.9: Clear intent — both sides changed but intent is unambiguous from context
45
+ - 0.6–0.7: Reasonable guess — requires interpretation of intent, some risk
46
+ - 0.3–0.5: Uncertain — ambiguous intent, multiple valid resolutions exist
47
+ - Cap whole-file strategies at 0.8 when both sides have changes. If one side is identical to base, 1.0 is appropriate.
48
+
27
49
  - "description": one or two sentences in plain English explaining WHAT was resolved and WHY. Do not give a chunk-by-chunk play-by-play.
28
50
  - "chunks": array of resolved chunks, one per conflict marker.
29
51
 
30
52
  Rules:
31
- - You must resolve ALL ${e.markers.length} marker(s). Every markerIndex from 0 to ${t} must appear exactly once in "chunks".
53
+ ${e.markers.length>0?`- You must resolve ALL ${e.markers.length} marker(s). Every markerIndex from 0 to ${e.markers.length-1} must appear exactly once in "chunks".
32
54
  - Use "ours" to keep the current branch version.
33
55
  - Use "theirs" to keep the incoming branch version.
34
56
  - Use "merged" with "content" when you need to combine or rewrite both sides. The content replaces only the conflict region (between <<<<<<< and >>>>>>>), not the entire file.
35
57
  - Do NOT include conflict markers (<<<<<<<, =======, >>>>>>>) in your resolved content.
36
- - Respond ONLY with the JSON object when you are ready. Do not wrap it in markdown code blocks.`}function n(e,t){let n=[],r=t.threeWayDiff;if(n.push(`File: ${e.filePath}`),n.push(``),t.commitMessage&&(n.push(`Commit message: ${t.commitMessage}`),n.push(``)),t.prDescription&&(n.push(`PR description: ${t.prDescription}`),n.push(``)),t.fileNeighbors&&t.fileNeighbors.length>0&&(n.push(`Related files in this directory: ${t.fileNeighbors.join(`, `)}`),n.push(``)),t.previousResolutions&&t.previousResolutions.length>0){n.push(`Previously resolved conflicts in this session:`);for(let e of t.previousResolutions)n.push(` - ${e.filePath}: ${e.strategy} (${e.description})`);n.push(``)}if(r){let e=t.refs?.ours??`ours`,i=t.refs?.theirs??`theirs`;n.push(`Three-way diffs for this file:`),n.push(``),n.push(`--- ours changes (base → ${e}) ---`),n.push(r.oursDiff),n.push(``),n.push(`--- theirs changes (base → ${i}) ---`),n.push(r.theirsDiff),n.push(``)}n.push(`Conflict markers (${e.markers.length} total):`),n.push(``);for(let t=0;t<e.markers.length;t++){let r=e.markers[t];r&&(n.push(`--- Marker ${t} (lines ${r.startLine}-${r.endLine}) ---`),r.contextBefore&&n.push(`Context before:\n${r.contextBefore}`),n.push(`<<<<<<< ${r.oursLabel}`),n.push(r.ours),r.base!==null&&(n.push(`||||||| ${r.baseLabel??`base`}`),n.push(r.base)),n.push(`=======`),n.push(r.theirs),n.push(`>>>>>>> ${r.theirsLabel}`),r.contextAfter&&n.push(`Context after:\n${r.contextAfter}`),n.push(``))}return n.join(`
58
+ `:``}- Respond ONLY with the JSON object when you are ready.
59
+
60
+ ## Delete-Modify Conflicts
61
+ When the user message says "Delete-modify conflict", one side deleted the file while the other modified it. There are NO conflict markers — only the three-way diff is available.
62
+ - Default to the side that deleted — the deletion was intentional and the modifications are usually obsolete.
63
+ - Keep the modified side ONLY when the three-way diff shows the modifications are clearly essential (e.g., the file is newly created, not just edited).
64
+ - To accept the deletion, use strategy matching the deleting side. To keep the file, use strategy matching the modifying side.
65
+ - Cap confidence at 0.8 — delete-modify conflicts always carry some risk.`}function n(e,t){let n=[],r=t.threeWayDiff;if(n.push(`File: ${e.filePath}`),n.push(``),t.commitMessage&&(n.push(`Commit message: ${t.commitMessage}`),n.push(``)),t.prDescription&&(n.push(`PR description: ${t.prDescription}`),n.push(``)),t.fileNeighbors&&t.fileNeighbors.length>0&&(n.push(`Related files in this directory: ${t.fileNeighbors.join(`, `)}`),n.push(``)),t.previousResolutions&&t.previousResolutions.length>0){n.push(`Previously resolved conflicts in this session:`);for(let e of t.previousResolutions)n.push(` - ${e.filePath}: ${e.strategy} (${e.description})`);n.push(``)}if(r){let e=t.refs?.ours??`ours`,i=t.refs?.theirs??`theirs`;n.push(`Three-way diffs for this file:`),n.push(``),n.push(`--- ours changes (base → ${e}) ---`),n.push(r.oursDiff),n.push(``),n.push(`--- theirs changes (base → ${i}) ---`),n.push(r.theirsDiff),n.push(``)}if(e.type===`delete-modify`){let t=e.deletedBy===`ours`?`theirs`:`ours`;n.push(`Delete-modify conflict: ${e.filePath}`),n.push(`The file was deleted by ${e.deletedBy} and modified by ${t}.`),n.push(`The "${e.deletedBy}" diff shows the deletion and the "${t}" diff shows the modifications.`),n.push(``)}else{n.push(`Conflict markers (${e.markers.length} total):`),n.push(``);for(let t=0;t<e.markers.length;t++){let r=e.markers[t];r&&(n.push(`--- Marker ${t} (lines ${r.startLine}-${r.endLine}) ---`),r.contextBefore&&n.push(`Context before:\n${r.contextBefore}`),n.push(`<<<<<<< ${r.oursLabel}`),n.push(r.ours),r.base!==null&&(n.push(`||||||| ${r.baseLabel??`base`}`),n.push(r.base)),n.push(`=======`),n.push(r.theirs),n.push(`>>>>>>> ${r.theirsLabel}`),r.contextAfter&&n.push(`Context after:\n${r.contextAfter}`),n.push(``))}}return n.join(`
37
66
  `)}export{e as buildPrompt};
@@ -1 +1 @@
1
- import{createResolution as e}from"../resolution/create-resolution.js";import{runResolverLoop as t}from"./loop.js";import{buildThreeWayDiff as n}from"./three-way-diff.js";async function r(r,i,a){let{chunks:o,confidence:s,description:c,metrics:l}=await t(r,i.threeWayDiff||!i.refs?i:{...i,threeWayDiff:await n(r.filePath,i.refs,{git:a.git,signal:a.signal})},{model:a.model,git:a.git,config:a.config,onProgress:a.onProgress,signal:a.signal}),u=e(r,o,{confidence:s,description:c,metrics:l});return a.onProgress?.({type:`resolver:completed`,filePath:r.filePath,stepCount:l.stepCount??0}),u}export{r as resolveConflict};
1
+ import{createResolution as e}from"../resolution/create-resolution.js";import{runResolverLoop as t}from"./loop.js";import{buildThreeWayDiff as n}from"./three-way-diff.js";async function r(r,i,a){let{chunks:o,strategy:s,confidence:c,description:l,metrics:u}=await t(r,i.threeWayDiff||!i.refs?i:{...i,threeWayDiff:await n(r.filePath,i.refs,{git:a.git,signal:a.signal})},{model:a.model,git:a.git,config:a.config,onProgress:a.onProgress,signal:a.signal,verifier:a.verifier}),d=e(r,o,{confidence:c,description:l,metrics:u,...s?{fileStrategy:s}:{}});return a.onProgress?.({type:`resolver:completed`,filePath:r.filePath,stepCount:u.stepCount??0}),d}export{r as resolveConflict};
@@ -1 +1 @@
1
- import{AIError as e}from"../errors/ai.js";import{unmergedEntries as t}from"../git/port-dispatch.js";import{extractConflict as n}from"../extraction/extract.js";import{takeOurs as r,takeTheirs as i}from"../resolution/take-strategy.js";import{resolveConflict as a}from"./resolve-conflict.js";import o from"picomatch";async function s(e,r){let i=e.config?.excludeFiles??[],a=i.length>0?o(i,{basename:!0}):()=>!1,s=(await t(e.git,{signal:e.signal})).filter(e=>!a(e.path)),u={...r??{}},d=[],f=[];for(let t of s){if(e.signal?.aborted)break;let r;try{r=await n(t.path,{git:e.git,signal:e.signal})}catch(e){f.push({filePath:t.path,error:l(e)});continue}if(!r)continue;e.onProgress?.({type:`conflict:found`,filePath:r.filePath,conflictType:r.type,markerCount:r.markers.length});let i=await c(r,u,e,f);i&&(u.previousResolutions=[...u.previousResolutions??[],i],d.push(i),e.onProgress?.({type:`resolution:applied`,filePath:i.filePath,strategy:i.strategy,confidence:i.confidence,description:i.description}))}return{resolutions:d,errors:f}}async function c(t,n,o,s){try{return await a(t,n,{model:o.model,git:o.git,config:o.config,onProgress:o.onProgress,signal:o.signal})}catch(n){let a=l(n),c=o.config?.fallbackStrategy;return c?(o.onProgress?.({type:`resolution:fallback`,filePath:t.filePath,error:a,cause:a instanceof e?a.code:void 0}),c===`take-ours`?r(t):i(t)):(s.push({filePath:t.filePath,error:a}),o.onProgress?.({type:`resolution:failed`,filePath:t.filePath,error:a}),null)}}function l(e){return e instanceof Error?e:Error(String(e))}export{s as resolveConflicts};
1
+ import{AIError as e}from"../errors/ai.js";import{unmergedEntries as t}from"../git/port-dispatch.js";import{extractConflict as n}from"../extraction/extract.js";import{takeOurs as r,takeTheirs as i}from"../resolution/take-strategy.js";import{resolveConflict as a}from"./resolve-conflict.js";import o from"picomatch";async function s(e,r){let i=e.config?.excludeFiles??[],a=i.length>0?o(i,{basename:!0}):()=>!1,s=await t(e.git,{signal:e.signal}),u={...r??{}},d=[],f=[],p=[];for(let t of s){if(e.signal?.aborted)break;if(a(t.path)){p.push({filePath:t.path,reason:`excluded`}),e.onProgress?.({type:`conflict:excluded`,filePath:t.path});continue}let r;try{r=await n(t.path,{git:e.git,signal:e.signal},t.reason)}catch(e){f.push({filePath:t.path,error:l(e)});continue}if(!r){p.push({filePath:t.path,reason:`no-markers`}),e.onProgress?.({type:`conflict:skipped`,filePath:t.path,reason:`no-markers`});continue}e.onProgress?.({type:`conflict:found`,filePath:r.filePath,conflictType:r.type,markerCount:r.markers.length});let i=await c(r,u,e,f);i&&(u.previousResolutions=[...u.previousResolutions??[],i],d.push(i),e.onProgress?.({type:`resolution:applied`,filePath:i.filePath,strategy:i.strategy,confidence:i.confidence,description:i.description}))}return{resolutions:d,errors:f,skipped:p}}async function c(t,n,o,s){try{return await a(t,n,{model:o.model,git:o.git,config:o.config,onProgress:o.onProgress,signal:o.signal,verifier:o.verifier})}catch(n){let a=l(n),c=o.config?.fallbackStrategy;return c?(o.onProgress?.({type:`resolution:fallback`,filePath:t.filePath,error:a,cause:a instanceof e?a.code:void 0}),c===`take-ours`?r(t):i(t)):(s.push({filePath:t.filePath,error:a}),o.onProgress?.({type:`resolution:failed`,filePath:t.filePath,error:a}),null)}}function l(e){return e instanceof Error?e:Error(String(e))}export{s as resolveConflicts};
@@ -1,5 +1,5 @@
1
1
  //#region src/types/conflict.d.ts
2
- type ConflictType = 'text';
2
+ type ConflictType = 'text' | 'delete-modify';
3
3
  interface ConflictMarker {
4
4
  startLine: number;
5
5
  endLine: number;
@@ -17,6 +17,7 @@ interface Conflict {
17
17
  markers: ConflictMarker[];
18
18
  type: ConflictType;
19
19
  rawContent: string;
20
+ deletedBy?: 'ours' | 'theirs';
20
21
  }
21
22
  //#endregion
22
23
  export { Conflict, ConflictMarker, ConflictType };
@@ -8,6 +8,13 @@ type ConflictProgressEvent = {
8
8
  filePath: string;
9
9
  conflictType: ConflictType;
10
10
  markerCount: number;
11
+ } | {
12
+ type: 'conflict:excluded';
13
+ filePath: string;
14
+ } | {
15
+ type: 'conflict:skipped';
16
+ filePath: string;
17
+ reason: string;
11
18
  } | {
12
19
  type: 'resolution:applied';
13
20
  filePath: string;
@@ -1,3 +1,4 @@
1
+ import { ConflictType } from "./conflict.js";
1
2
  import { AiTokenUsage } from "@gitkraken/shared-tools";
2
3
 
3
4
  //#region src/types/resolution.d.ts
@@ -19,6 +20,8 @@ interface Resolution {
19
20
  note?: string;
20
21
  chunks?: ResolvedChunk[];
21
22
  metrics?: ResolutionMetrics;
23
+ conflictType?: ConflictType;
24
+ deletedBy?: 'ours' | 'theirs';
22
25
  }
23
26
  interface ResolutionRefs {
24
27
  ours: string;
@@ -8,6 +8,10 @@ interface StepResult {
8
8
  filePath: string;
9
9
  error: Error;
10
10
  }[];
11
+ skipped?: {
12
+ filePath: string;
13
+ reason: string;
14
+ }[];
11
15
  }
12
16
  interface StepConfig extends ResolverConfig {
13
17
  excludeFiles?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitkraken/conflict-tools",
3
- "version": "0.0.1-beta.4",
3
+ "version": "0.0.1-beta.6",
4
4
  "description": "AI-powered git conflict resolution.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Axosoft, LLC dba GitKraken",