diff --git a/NEW/src/Utils/JdeScoping.ConfigManager/Services/DiffService.cs b/NEW/src/Utils/JdeScoping.ConfigManager/Services/DiffService.cs
new file mode 100644
index 0000000..b5974a5
--- /dev/null
+++ b/NEW/src/Utils/JdeScoping.ConfigManager/Services/DiffService.cs
@@ -0,0 +1,65 @@
+using DiffPlex;
+using DiffPlex.DiffBuilder;
+using DiffPlex.DiffBuilder.Model;
+
+namespace JdeScoping.ConfigManager.Services;
+
+///
+/// Service for generating diffs between text content.
+///
+public class DiffService : IDiffService
+{
+ public DiffResult GenerateDiff(string original, string modified)
+ {
+ var diffBuilder = new InlineDiffBuilder(new Differ());
+ var diff = diffBuilder.BuildDiffModel(original, modified);
+
+ var lines = new List();
+ int oldLineNum = 1;
+ int newLineNum = 1;
+ int insertions = 0;
+ int deletions = 0;
+
+ foreach (var line in diff.Lines)
+ {
+ var diffLine = new DiffLine
+ {
+ Text = line.Text,
+ Type = line.Type switch
+ {
+ ChangeType.Inserted => DiffLineType.Added,
+ ChangeType.Deleted => DiffLineType.Removed,
+ _ => DiffLineType.Unchanged
+ },
+ OldLineNumber = line.Type == ChangeType.Inserted ? null : oldLineNum,
+ NewLineNumber = line.Type == ChangeType.Deleted ? null : newLineNum
+ };
+
+ lines.Add(diffLine);
+
+ switch (line.Type)
+ {
+ case ChangeType.Inserted:
+ newLineNum++;
+ insertions++;
+ break;
+ case ChangeType.Deleted:
+ oldLineNum++;
+ deletions++;
+ break;
+ default:
+ oldLineNum++;
+ newLineNum++;
+ break;
+ }
+ }
+
+ return new DiffResult
+ {
+ HasChanges = insertions > 0 || deletions > 0,
+ Lines = lines,
+ Insertions = insertions,
+ Deletions = deletions
+ };
+ }
+}
diff --git a/NEW/src/Utils/JdeScoping.ConfigManager/Services/IDiffService.cs b/NEW/src/Utils/JdeScoping.ConfigManager/Services/IDiffService.cs
new file mode 100644
index 0000000..502edaa
--- /dev/null
+++ b/NEW/src/Utils/JdeScoping.ConfigManager/Services/IDiffService.cs
@@ -0,0 +1,38 @@
+namespace JdeScoping.ConfigManager.Services;
+
+///
+/// Represents a line in a diff output.
+///
+public class DiffLine
+{
+ public required int? OldLineNumber { get; init; }
+ public required int? NewLineNumber { get; init; }
+ public required string Text { get; init; }
+ public required DiffLineType Type { get; init; }
+}
+
+public enum DiffLineType
+{
+ Unchanged,
+ Added,
+ Removed
+}
+
+///
+/// Result of a diff operation.
+///
+public class DiffResult
+{
+ public bool HasChanges { get; init; }
+ public List Lines { get; init; } = [];
+ public int Insertions { get; init; }
+ public int Deletions { get; init; }
+}
+
+///
+/// Service for generating diffs between text content.
+///
+public interface IDiffService
+{
+ DiffResult GenerateDiff(string original, string modified);
+}
diff --git a/NEW/tests/JdeScoping.ConfigManager.Tests/Services/DiffServiceTests.cs b/NEW/tests/JdeScoping.ConfigManager.Tests/Services/DiffServiceTests.cs
new file mode 100644
index 0000000..cba412f
--- /dev/null
+++ b/NEW/tests/JdeScoping.ConfigManager.Tests/Services/DiffServiceTests.cs
@@ -0,0 +1,173 @@
+using JdeScoping.ConfigManager.Services;
+
+namespace JdeScoping.ConfigManager.Tests.Services;
+
+public class DiffServiceTests
+{
+ private readonly DiffService _sut;
+
+ public DiffServiceTests()
+ {
+ _sut = new DiffService();
+ }
+
+ [Fact]
+ public void GenerateDiff_WithNoChanges_ReturnsEmptyDiff()
+ {
+ // Arrange
+ var original = "line1\nline2\nline3";
+ var modified = "line1\nline2\nline3";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void GenerateDiff_WithChanges_ReturnsDiffLines()
+ {
+ // Arrange
+ var original = "line1\nline2\nline3";
+ var modified = "line1\nmodified\nline3";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeTrue();
+ result.Lines.ShouldNotBeEmpty();
+ }
+
+ [Fact]
+ public void GenerateDiff_WithAddedLine_ReportsInsertion()
+ {
+ // Arrange
+ var original = "line1\nline2";
+ var modified = "line1\nline2\nline3";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeTrue();
+ result.Insertions.ShouldBe(1);
+ result.Deletions.ShouldBe(0);
+ result.Lines.ShouldContain(l => l.Type == DiffLineType.Added && l.Text == "line3");
+ }
+
+ [Fact]
+ public void GenerateDiff_WithRemovedLine_ReportsDeletion()
+ {
+ // Arrange
+ var original = "line1\nline2\nline3";
+ var modified = "line1\nline3";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeTrue();
+ result.Deletions.ShouldBe(1);
+ result.Lines.ShouldContain(l => l.Type == DiffLineType.Removed && l.Text == "line2");
+ }
+
+ [Fact]
+ public void GenerateDiff_WithModifiedLine_ReportsAdditionAndDeletion()
+ {
+ // Arrange
+ var original = "line1\noriginal\nline3";
+ var modified = "line1\nchanged\nline3";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeTrue();
+ result.Insertions.ShouldBeGreaterThan(0);
+ result.Deletions.ShouldBeGreaterThan(0);
+ }
+
+ [Fact]
+ public void GenerateDiff_WithEmptyOriginal_ReportsAllAsInsertions()
+ {
+ // Arrange
+ var original = "";
+ var modified = "line1\nline2";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeTrue();
+ result.Insertions.ShouldBeGreaterThan(0);
+ result.Deletions.ShouldBe(0);
+ }
+
+ [Fact]
+ public void GenerateDiff_WithEmptyModified_ReportsAllAsDeletions()
+ {
+ // Arrange
+ var original = "line1\nline2";
+ var modified = "";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ result.HasChanges.ShouldBeTrue();
+ result.Insertions.ShouldBe(0);
+ result.Deletions.ShouldBeGreaterThan(0);
+ }
+
+ [Fact]
+ public void GenerateDiff_LineNumbers_AreCorrectForUnchangedLines()
+ {
+ // Arrange
+ var original = "line1\nline2\nline3";
+ var modified = "line1\nline2\nline3";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ var firstLine = result.Lines.First();
+ firstLine.OldLineNumber.ShouldBe(1);
+ firstLine.NewLineNumber.ShouldBe(1);
+ }
+
+ [Fact]
+ public void GenerateDiff_AddedLine_HasNullOldLineNumber()
+ {
+ // Arrange
+ var original = "line1";
+ var modified = "line1\nline2";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ var addedLine = result.Lines.FirstOrDefault(l => l.Type == DiffLineType.Added);
+ addedLine.ShouldNotBeNull();
+ addedLine.OldLineNumber.ShouldBeNull();
+ addedLine.NewLineNumber.ShouldNotBeNull();
+ }
+
+ [Fact]
+ public void GenerateDiff_RemovedLine_HasNullNewLineNumber()
+ {
+ // Arrange
+ var original = "line1\nline2";
+ var modified = "line1";
+
+ // Act
+ var result = _sut.GenerateDiff(original, modified);
+
+ // Assert
+ var removedLine = result.Lines.FirstOrDefault(l => l.Type == DiffLineType.Removed);
+ removedLine.ShouldNotBeNull();
+ removedLine.OldLineNumber.ShouldNotBeNull();
+ removedLine.NewLineNumber.ShouldBeNull();
+ }
+}