Skip to content

aaditya-paul/intentmerge-cli

Repository files navigation

IntentMerge: Deterministic Semantic Merge Resolution

IntentMerge is a production-grade, AST-based CLI tool designed to replace Git's standard line-based merge conflict resolution for TypeScript and React (JSX/TSX) projects.

Traditional Git merging operates on raw text lines. If two developers modify the same lineβ€”even if one only added formatting and the other changed logicβ€”Git flags a conflict. IntentMerge fixes this by understanding the semantic intent of the code.

By parsing code into an Abstract Syntax Tree (AST), compiling the structural diffs, and applying deterministic resolution rules, IntentMerge guarantees safe automerges and highlights exactly why a conflict is dangerous.


🎯 Core Philosophy & Key Features

  1. AST-Based Understanding: We don't compare strings; we compare tree nodes. A change in indentation or trailing commas literally doesn't exist to IntentMerge's diffing engine.
  2. Intent Classification: We categorize changes. Did the developer rename a variable? Add a JSX prop? Modify a hook dependency array? Each receives a specific label (e.g., rename, hook dependency change).
  3. Deterministic Auto-Merge: Safe intersections (like developer A adding a prop <Button size="lg" />, and developer B adding a prop <Button color="red" />) are mathematically and deterministically merged.
  4. Validation-First: No silent logic corruption. Every successful AST merge is written to disk and immediately checked using tsc --noEmit. If TypeScript compilation fails, the merge is rolled back to manual intervention.
  5. No Core LLM Hallucinations: Resolution logic is strictly programmatic. LLMs are never used to force an uncontrolled code mutation.

🧱 Architecture Deep Dive

IntentMerge operates through a 6-stage pipeline:

1. Parser Wrapper (src/parser/index.ts)

We use @babel/parser to ingest the base file and the two divergent branches (A and B).

  • Format Normalization: We strip loc, start, end, and extra properties from the Babel AST. This ensures that formatting noise (spaces, line-breaks) is completely ignored during deep equality comparisons.

2. Diff Engine (src/diff/index.ts)

A recursive tree traversal compares the normalized base AST against versions A and B.

  • It produces a unified AstChange interface containing { nodeType, path, changeType: 'added' | 'removed' | 'modified', baseNode, branchNode }.
  • Note: The MVP currently relies on indexing for arrays without clear unique identifiers.

3. Change Classifier (src/classifier/index.ts)

Transforms raw node differences into semantic human-readable intents (ChangeClass).

  • Maps JSXAttribute changes to prop modification.
  • Maps modification of useEffect arguments to hook dependency change.
  • Maps Identifier node replacements to rename.
  • Maps binary, logical, and execution blocks to function logic modification.

4. Conflict Resolver (src/resolver/index.ts)

The intelligence core. It iterates over overlapping change paths and applies a strict safety matrix:

  • βœ… Safe (Auto-merge): rename vs function logic modification.
  • βœ… Safe (Auto-merge): formatting vs anything.
  • βœ… Safe (Auto-merge): Mutating entirely different JSX props on the same element.
  • ❌ Unsafe (Manual): function logic modification vs function logic modification (both branches changed the inner workings of the exact same function differently).
  • ❌ Unsafe (Manual): hook dependency change vs hook dependency change.

It outputs a Result containing a confidenceScore and a boolean resolutionType: 'auto-safe' | 'manual-required'.

5. Code Patch Generator (src/generator/index.ts)

If the merge is auto-safe, the Generator applies the safely isolated A and B AST modifications back onto the base AST tree. It then uses @babel/generator to emit clean, minified TypeScript code. Since Babel generator output can be aggressively dense, we pipe the result through Prettier to restore standard developer formatting.

6. Validation Engine (src/validator/index.ts)

The formatted code is written to a hidden temporary file (.temp-validate-merge.tsx). We spawn a child process running npx tsc --noEmit against it.

  • If it passes: The resolution is finalized and written to the output destination.
  • If it fails: The CLI throws a strict validation error and alerts the developer.

πŸš€ Getting Started

Installation

Clone the repository and install dependencies:

git clone <repo>
cd intentmerge
npm install
npm run build
npm link

Note: npm link allows you to run the intentmerge command globally in your terminal.

Usage

Use the CLI to resolve a 3-way conflict. Provide the common ancestor (--base), your current branch (--branchA), the incoming branch (--branchB), and where you want the successfully merged file to go (--output).

npx intentmerge resolve -b base.tsx -a branchA.tsx -c branchB.tsx -o output.tsx

Or if installed globally via npm link:

intentmerge resolve -b base.tsx -a branchA.tsx -c branchB.tsx -o output.tsx

CLI Output Example

Parsing ASTs...
Diffing base to Branch A...
Diffing base to Branch B...
Classifying differences...
Resolving conflicts...

================ RESOLUTION SUMMARY ================
Confidence Score: 100%
Resolution Type:  auto-safe
Safe Changes:     2
Unresolved/Conflicts: 0
====================================================

Applying safe changes & Generating code...
Validating output TS code...
βœ… Validation passed. Writing resolved file to: /path/to/output.tsx

πŸ§ͺ Testing

The resolution matrix and Diffing engine are heavily unit-tested.

  1. Unit tests: Test suite covering isolated classifications and mock resolution matrices (e.g. asserting that Renames merge with Logic, but overlapping Logic mutations throw conflicts).
npm test
  1. E2E Mock Testing: You can test the actual CLI behavior by running the script against the provided mock files in the repository root:
intentmerge resolve -b mock-base.tsx -a mock-a.tsx -c mock-b.tsx -o res.tsx

⚠️ Known MVP Limitations & Safety Boundaries

This is a Minimum Viable Product demonstrating programmatic semantic intent.

  • Array matching: Complex structural additions/removals requiring parent context tracking (e.g., adding a JSX child in the middle of a list of children without a strict ID) currently fall back to index matching which can be noisy. This is flagged in logs.
  • Comment nodes conflict: Comments in source code are stored in the AST just like code. If both branches add different comments to the same node (e.g., // Branch A: ... and // Branch B: ...), IntentMerge will correctly flag these as conflicts β€” even if the underlying logic is different. This is by design: a comment describing a change is semantically part of that change.
  • Cross-File Resolution: IntentMerge currently analyzes isolated, single files. Cross-file refactor dependencies (e.g. changing an exported Type in A.ts and referencing it in B.ts) are left to standard integration checks.
  • CSS: Does not support CSS/SCSS module merging.

πŸ€– LLM Advisory Mode

When a merge is flagged as manual-required, IntentMerge automatically detects whether an LLM is available and offers to explain the conflicts and suggest how to resolve them.

How it works

When you see ⚠️ Manual conflict resolution required, the CLI will check for providers in order:

Priority Provider Trigger
1st Gemini GEMINI_API_KEY or GOOGLE_API_KEY env var
2nd Groq GROQ_API_KEY env var
3rd Local LLM Ollama running on localhost:11434

If a provider is found, you'll be prompted:

πŸ€– Would you like LLM advisory via Gemini (GEMINI_API_KEY detected)? (y/n):

If you answer y, the tool sends all three file versions + the classified conflict list to the LLM and prints structured advice:

────────────────────────────────────────────────────────────
🧠 LLM Advisory (GEMINI)
────────────────────────────────────────────────────────────
[Explanation of each conflict and a proposed merge strategy]
────────────────────────────────────────────────────────────

Setup

# Use Gemini (recommended)
export GEMINI_API_KEY=your_key_here

# Or Groq (free tier available)
export GROQ_API_KEY=your_key_here

# Or run Ollama locally (no API key needed)
ollama serve
ollama pull llama3   # or any model
# IntentMerge auto-detects Ollama on localhost:11434

Note: LLM advisory is purely informational. It never automatically applies any code changes β€” that would violate the no-hallucination guarantee. You always stay in control.


πŸ’‘ Real-World Conflict Analysis

Example: use-debounce manual-required result

This scenario (from examples/hooks/use-debounce/) demonstrates a case where both branches appear to make "light" changes β€” but IntentMerge correctly flags 4 conflicts:

Branch A replaced the internal timer variable with a useRef (a more idiomatic React pattern for persisting values across renders without triggering re-renders).

Branch B introduced a safeDelay = Math.max(100, delay) floor and used it in both the setTimeout call and the useEffect dependency array.

Both changes modified useEffect's body at the same array positions (body[1] and body[2]), making them structurally irreconcilable:

Branch A Branch B
body[1] if (timeoutRef.current) clearTimeout(...) const timer = setTimeout(..., safeDelay)
body[2] timeoutRef.current = setTimeout(...) return () => clearTimeout(timer)
Deps [value, delay] [value, safeDelay]

Additionally, both branches added different // Branch X: leading comments to the same AST node β€” and since comments live in the AST, they were flagged as comment-node conflicts too.

Confidence score: 20% β€” correctly low because the majority of the function body was in unresolvable conflict.

This is IntentMerge working as designed: it refuses to guess which developer's logic is "more correct."


🧠 Roadmap

  1. βœ… LLM Advisory Mode: Implemented β€” uses Gemini, Groq, or local Ollama when manual-required.
  2. Abstract Object ID mapping: Enhancing the Diff engine to recognize React key props and AST id declarations to completely ignore array index shifts during diffing.
  3. Cross-file resolution: Tracking type exports and usages across files.

About

production-grade, AST-based CLI tool designed to replace Git's standard line-based merge conflict resolution for TypeScript and React (JSX/TSX) projects.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors