newo 2.0.5 → 2.0.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.
- package/dist/sync/diff-utils.d.ts +18 -0
- package/dist/sync/diff-utils.js +152 -0
- package/dist/sync/skill-files.js +22 -49
- package/package.json +1 -1
- package/src/sync/diff-utils.ts +168 -0
- package/src/sync/skill-files.ts +22 -57
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proper diff algorithm for accurate content comparison
|
|
3
|
+
*/
|
|
4
|
+
export interface DiffLine {
|
|
5
|
+
type: 'context' | 'add' | 'remove';
|
|
6
|
+
localLineNum: number;
|
|
7
|
+
remoteLineNum: number;
|
|
8
|
+
content: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generate a proper diff using LCS algorithm
|
|
12
|
+
*/
|
|
13
|
+
export declare function generateDiff(localContent: string, remoteContent: string): DiffLine[];
|
|
14
|
+
/**
|
|
15
|
+
* Filter diff to show only relevant sections with context
|
|
16
|
+
*/
|
|
17
|
+
export declare function filterDiffWithContext(diff: DiffLine[], contextLines?: number): DiffLine[];
|
|
18
|
+
//# sourceMappingURL=diff-utils.d.ts.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proper diff algorithm for accurate content comparison
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Generate a proper diff using LCS algorithm
|
|
6
|
+
*/
|
|
7
|
+
export function generateDiff(localContent, remoteContent) {
|
|
8
|
+
const localLines = localContent.trim().split('\n');
|
|
9
|
+
const remoteLines = remoteContent.trim().split('\n');
|
|
10
|
+
// Simple LCS-based diff algorithm
|
|
11
|
+
const diff = [];
|
|
12
|
+
let localIdx = 0;
|
|
13
|
+
let remoteIdx = 0;
|
|
14
|
+
while (localIdx < localLines.length || remoteIdx < remoteLines.length) {
|
|
15
|
+
const localLine = localLines[localIdx];
|
|
16
|
+
const remoteLine = remoteLines[remoteIdx];
|
|
17
|
+
if (localIdx >= localLines.length) {
|
|
18
|
+
// Only remote lines left (additions)
|
|
19
|
+
diff.push({
|
|
20
|
+
type: 'add',
|
|
21
|
+
localLineNum: -1,
|
|
22
|
+
remoteLineNum: remoteIdx + 1,
|
|
23
|
+
content: remoteLine || ''
|
|
24
|
+
});
|
|
25
|
+
remoteIdx++;
|
|
26
|
+
}
|
|
27
|
+
else if (remoteIdx >= remoteLines.length) {
|
|
28
|
+
// Only local lines left (deletions)
|
|
29
|
+
diff.push({
|
|
30
|
+
type: 'remove',
|
|
31
|
+
localLineNum: localIdx + 1,
|
|
32
|
+
remoteLineNum: -1,
|
|
33
|
+
content: localLine || ''
|
|
34
|
+
});
|
|
35
|
+
localIdx++;
|
|
36
|
+
}
|
|
37
|
+
else if (localLine === remoteLine) {
|
|
38
|
+
// Lines match (context)
|
|
39
|
+
diff.push({
|
|
40
|
+
type: 'context',
|
|
41
|
+
localLineNum: localIdx + 1,
|
|
42
|
+
remoteLineNum: remoteIdx + 1,
|
|
43
|
+
content: localLine || ''
|
|
44
|
+
});
|
|
45
|
+
localIdx++;
|
|
46
|
+
remoteIdx++;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Lines differ - check if this is an insertion or substitution
|
|
50
|
+
// Look ahead to see if the local line appears later in remote
|
|
51
|
+
let foundInRemote = -1;
|
|
52
|
+
for (let j = remoteIdx + 1; j < Math.min(remoteIdx + 5, remoteLines.length); j++) {
|
|
53
|
+
if (localLine === remoteLines[j]) {
|
|
54
|
+
foundInRemote = j;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Look ahead to see if the remote line appears later in local
|
|
59
|
+
let foundInLocal = -1;
|
|
60
|
+
for (let j = localIdx + 1; j < Math.min(localIdx + 5, localLines.length); j++) {
|
|
61
|
+
if (remoteLine === localLines[j]) {
|
|
62
|
+
foundInLocal = j;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (foundInRemote !== -1 && (foundInLocal === -1 || foundInRemote - remoteIdx < foundInLocal - localIdx)) {
|
|
67
|
+
// Local line found later in remote - this is likely remote insertions
|
|
68
|
+
while (remoteIdx < foundInRemote) {
|
|
69
|
+
diff.push({
|
|
70
|
+
type: 'add',
|
|
71
|
+
localLineNum: -1,
|
|
72
|
+
remoteLineNum: remoteIdx + 1,
|
|
73
|
+
content: remoteLines[remoteIdx] || ''
|
|
74
|
+
});
|
|
75
|
+
remoteIdx++;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (foundInLocal !== -1) {
|
|
79
|
+
// Remote line found later in local - this is likely local insertions (deletions in diff)
|
|
80
|
+
while (localIdx < foundInLocal) {
|
|
81
|
+
diff.push({
|
|
82
|
+
type: 'remove',
|
|
83
|
+
localLineNum: localIdx + 1,
|
|
84
|
+
remoteLineNum: -1,
|
|
85
|
+
content: localLines[localIdx] || ''
|
|
86
|
+
});
|
|
87
|
+
localIdx++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
// Lines are genuinely different (substitution)
|
|
92
|
+
diff.push({
|
|
93
|
+
type: 'remove',
|
|
94
|
+
localLineNum: localIdx + 1,
|
|
95
|
+
remoteLineNum: -1,
|
|
96
|
+
content: localLine || ''
|
|
97
|
+
});
|
|
98
|
+
diff.push({
|
|
99
|
+
type: 'add',
|
|
100
|
+
localLineNum: -1,
|
|
101
|
+
remoteLineNum: remoteIdx + 1,
|
|
102
|
+
content: remoteLine || ''
|
|
103
|
+
});
|
|
104
|
+
localIdx++;
|
|
105
|
+
remoteIdx++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return diff;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Filter diff to show only relevant sections with context
|
|
113
|
+
*/
|
|
114
|
+
export function filterDiffWithContext(diff, contextLines = 2) {
|
|
115
|
+
const result = [];
|
|
116
|
+
// Find all non-context lines (changes)
|
|
117
|
+
const changeIndices = [];
|
|
118
|
+
diff.forEach((line, idx) => {
|
|
119
|
+
if (line.type !== 'context') {
|
|
120
|
+
changeIndices.push(idx);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
if (changeIndices.length === 0) {
|
|
124
|
+
return []; // No changes
|
|
125
|
+
}
|
|
126
|
+
// Group nearby changes and add context
|
|
127
|
+
const groups = [];
|
|
128
|
+
let currentGroup = { start: changeIndices[0], end: changeIndices[0] };
|
|
129
|
+
for (let i = 1; i < changeIndices.length; i++) {
|
|
130
|
+
const idx = changeIndices[i];
|
|
131
|
+
if (idx - currentGroup.end <= contextLines * 2 + 1) {
|
|
132
|
+
// Close enough to current group, extend it
|
|
133
|
+
currentGroup.end = idx;
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Start new group
|
|
137
|
+
groups.push(currentGroup);
|
|
138
|
+
currentGroup = { start: idx, end: idx };
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
groups.push(currentGroup);
|
|
142
|
+
// Build result with context
|
|
143
|
+
for (const group of groups) {
|
|
144
|
+
const start = Math.max(0, group.start - contextLines);
|
|
145
|
+
const end = Math.min(diff.length - 1, group.end + contextLines);
|
|
146
|
+
for (let i = start; i <= end; i++) {
|
|
147
|
+
result.push(diff[i]);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=diff-utils.js.map
|
package/dist/sync/skill-files.js
CHANGED
|
@@ -107,62 +107,35 @@ export async function askForOverwrite(skillIdn, existingContent, newContent, fil
|
|
|
107
107
|
const greenBg = '\x1b[102m\x1b[30m'; // Light green background, black text (like GitHub)
|
|
108
108
|
const gray = '\x1b[90m';
|
|
109
109
|
const reset = '\x1b[0m';
|
|
110
|
-
//
|
|
111
|
-
const
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
let diffEnd = -1;
|
|
116
|
-
// Find first difference
|
|
117
|
-
for (let i = 0; i < Math.max(localLines.length, remoteLines.length); i++) {
|
|
118
|
-
if (localLines[i] !== remoteLines[i]) {
|
|
119
|
-
diffStart = i;
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
if (diffStart === -1) {
|
|
124
|
-
// No differences found (shouldn't happen, but handle gracefully)
|
|
110
|
+
// Generate proper diff using LCS algorithm
|
|
111
|
+
const { generateDiff, filterDiffWithContext } = await import('./diff-utils.js');
|
|
112
|
+
const fullDiff = generateDiff(existingContent, newContent);
|
|
113
|
+
const contextDiff = filterDiffWithContext(fullDiff, 2);
|
|
114
|
+
if (contextDiff.length === 0) {
|
|
125
115
|
console.log(`${gray} No differences found${reset}`);
|
|
126
116
|
return 'no';
|
|
127
117
|
}
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
diffEnd++;
|
|
135
|
-
}
|
|
136
|
-
// Show context before (2 lines)
|
|
137
|
-
const contextStart = Math.max(0, diffStart - 2);
|
|
138
|
-
for (let i = contextStart; i < diffStart; i++) {
|
|
139
|
-
if (localLines[i] !== undefined) {
|
|
140
|
-
console.log(` ${String(i + 1).padStart(3)} ${localLines[i]}`);
|
|
118
|
+
// Display the diff with proper GitHub-style formatting
|
|
119
|
+
for (const line of contextDiff) {
|
|
120
|
+
if (line.type === 'context') {
|
|
121
|
+
// Show context lines in gray
|
|
122
|
+
const lineNum = line.localLineNum !== -1 ? line.localLineNum : line.remoteLineNum;
|
|
123
|
+
console.log(` ${String(lineNum).padStart(3)} ${line.content}`);
|
|
141
124
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
for (let i = diffStart; i < maxDiffLine; i++) {
|
|
146
|
-
const localLine = localLines[i];
|
|
147
|
-
const remoteLine = remoteLines[i];
|
|
148
|
-
if (localLine !== undefined && (remoteLine === undefined || localLine !== remoteLine)) {
|
|
149
|
-
console.log(`${redBg} - ${String(i + 1).padStart(3)} ${localLine} ${reset}`);
|
|
150
|
-
}
|
|
151
|
-
if (remoteLine !== undefined && (localLine === undefined || localLine !== remoteLine)) {
|
|
152
|
-
console.log(`${greenBg} + ${String(i + 1).padStart(3)} ${remoteLine} ${reset}`);
|
|
125
|
+
else if (line.type === 'remove') {
|
|
126
|
+
// Show local content being removed (red background)
|
|
127
|
+
console.log(`${redBg} - ${String(line.localLineNum).padStart(3)} ${line.content} ${reset}`);
|
|
153
128
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
for (let i = diffEnd; i < contextEnd; i++) {
|
|
158
|
-
if (localLines[i] !== undefined) {
|
|
159
|
-
console.log(` ${String(i + 1).padStart(3)} ${localLines[i]}`);
|
|
129
|
+
else if (line.type === 'add') {
|
|
130
|
+
// Show remote content being added (green background)
|
|
131
|
+
console.log(`${greenBg} + ${String(line.remoteLineNum).padStart(3)} ${line.content} ${reset}`);
|
|
160
132
|
}
|
|
161
133
|
}
|
|
162
|
-
// Show if there are more
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
134
|
+
// Show if there are more changes beyond what we're displaying
|
|
135
|
+
const totalChanges = fullDiff.filter(line => line.type !== 'context').length;
|
|
136
|
+
const displayedChanges = contextDiff.filter(line => line.type !== 'context').length;
|
|
137
|
+
if (totalChanges > displayedChanges) {
|
|
138
|
+
console.log(`${gray}... (${totalChanges - displayedChanges} more changes)${reset}`);
|
|
166
139
|
}
|
|
167
140
|
const answer = await new Promise((resolve) => {
|
|
168
141
|
rl.question(`\nReplace local with remote? (y)es/(n)o/(a)ll/(q)uit: `, resolve);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "newo",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "NEWO CLI: Professional command-line tool with modular architecture for NEWO AI Agent development. Features IDN-based file management, real-time progress tracking, intelligent sync operations, and comprehensive multi-customer support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proper diff algorithm for accurate content comparison
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface DiffLine {
|
|
6
|
+
type: 'context' | 'add' | 'remove';
|
|
7
|
+
localLineNum: number;
|
|
8
|
+
remoteLineNum: number;
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generate a proper diff using LCS algorithm
|
|
14
|
+
*/
|
|
15
|
+
export function generateDiff(localContent: string, remoteContent: string): DiffLine[] {
|
|
16
|
+
const localLines = localContent.trim().split('\n');
|
|
17
|
+
const remoteLines = remoteContent.trim().split('\n');
|
|
18
|
+
|
|
19
|
+
// Simple LCS-based diff algorithm
|
|
20
|
+
const diff: DiffLine[] = [];
|
|
21
|
+
|
|
22
|
+
let localIdx = 0;
|
|
23
|
+
let remoteIdx = 0;
|
|
24
|
+
|
|
25
|
+
while (localIdx < localLines.length || remoteIdx < remoteLines.length) {
|
|
26
|
+
const localLine = localLines[localIdx];
|
|
27
|
+
const remoteLine = remoteLines[remoteIdx];
|
|
28
|
+
|
|
29
|
+
if (localIdx >= localLines.length) {
|
|
30
|
+
// Only remote lines left (additions)
|
|
31
|
+
diff.push({
|
|
32
|
+
type: 'add',
|
|
33
|
+
localLineNum: -1,
|
|
34
|
+
remoteLineNum: remoteIdx + 1,
|
|
35
|
+
content: remoteLine || ''
|
|
36
|
+
});
|
|
37
|
+
remoteIdx++;
|
|
38
|
+
} else if (remoteIdx >= remoteLines.length) {
|
|
39
|
+
// Only local lines left (deletions)
|
|
40
|
+
diff.push({
|
|
41
|
+
type: 'remove',
|
|
42
|
+
localLineNum: localIdx + 1,
|
|
43
|
+
remoteLineNum: -1,
|
|
44
|
+
content: localLine || ''
|
|
45
|
+
});
|
|
46
|
+
localIdx++;
|
|
47
|
+
} else if (localLine === remoteLine) {
|
|
48
|
+
// Lines match (context)
|
|
49
|
+
diff.push({
|
|
50
|
+
type: 'context',
|
|
51
|
+
localLineNum: localIdx + 1,
|
|
52
|
+
remoteLineNum: remoteIdx + 1,
|
|
53
|
+
content: localLine || ''
|
|
54
|
+
});
|
|
55
|
+
localIdx++;
|
|
56
|
+
remoteIdx++;
|
|
57
|
+
} else {
|
|
58
|
+
// Lines differ - check if this is an insertion or substitution
|
|
59
|
+
// Look ahead to see if the local line appears later in remote
|
|
60
|
+
let foundInRemote = -1;
|
|
61
|
+
for (let j = remoteIdx + 1; j < Math.min(remoteIdx + 5, remoteLines.length); j++) {
|
|
62
|
+
if (localLine === remoteLines[j]) {
|
|
63
|
+
foundInRemote = j;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Look ahead to see if the remote line appears later in local
|
|
69
|
+
let foundInLocal = -1;
|
|
70
|
+
for (let j = localIdx + 1; j < Math.min(localIdx + 5, localLines.length); j++) {
|
|
71
|
+
if (remoteLine === localLines[j]) {
|
|
72
|
+
foundInLocal = j;
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (foundInRemote !== -1 && (foundInLocal === -1 || foundInRemote - remoteIdx < foundInLocal - localIdx)) {
|
|
78
|
+
// Local line found later in remote - this is likely remote insertions
|
|
79
|
+
while (remoteIdx < foundInRemote) {
|
|
80
|
+
diff.push({
|
|
81
|
+
type: 'add',
|
|
82
|
+
localLineNum: -1,
|
|
83
|
+
remoteLineNum: remoteIdx + 1,
|
|
84
|
+
content: remoteLines[remoteIdx] || ''
|
|
85
|
+
});
|
|
86
|
+
remoteIdx++;
|
|
87
|
+
}
|
|
88
|
+
} else if (foundInLocal !== -1) {
|
|
89
|
+
// Remote line found later in local - this is likely local insertions (deletions in diff)
|
|
90
|
+
while (localIdx < foundInLocal) {
|
|
91
|
+
diff.push({
|
|
92
|
+
type: 'remove',
|
|
93
|
+
localLineNum: localIdx + 1,
|
|
94
|
+
remoteLineNum: -1,
|
|
95
|
+
content: localLines[localIdx] || ''
|
|
96
|
+
});
|
|
97
|
+
localIdx++;
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
// Lines are genuinely different (substitution)
|
|
101
|
+
diff.push({
|
|
102
|
+
type: 'remove',
|
|
103
|
+
localLineNum: localIdx + 1,
|
|
104
|
+
remoteLineNum: -1,
|
|
105
|
+
content: localLine || ''
|
|
106
|
+
});
|
|
107
|
+
diff.push({
|
|
108
|
+
type: 'add',
|
|
109
|
+
localLineNum: -1,
|
|
110
|
+
remoteLineNum: remoteIdx + 1,
|
|
111
|
+
content: remoteLine || ''
|
|
112
|
+
});
|
|
113
|
+
localIdx++;
|
|
114
|
+
remoteIdx++;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return diff;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Filter diff to show only relevant sections with context
|
|
124
|
+
*/
|
|
125
|
+
export function filterDiffWithContext(diff: DiffLine[], contextLines: number = 2): DiffLine[] {
|
|
126
|
+
const result: DiffLine[] = [];
|
|
127
|
+
|
|
128
|
+
// Find all non-context lines (changes)
|
|
129
|
+
const changeIndices: number[] = [];
|
|
130
|
+
diff.forEach((line, idx) => {
|
|
131
|
+
if (line.type !== 'context') {
|
|
132
|
+
changeIndices.push(idx);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (changeIndices.length === 0) {
|
|
137
|
+
return []; // No changes
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Group nearby changes and add context
|
|
141
|
+
const groups: Array<{start: number, end: number}> = [];
|
|
142
|
+
let currentGroup = { start: changeIndices[0]!, end: changeIndices[0]! };
|
|
143
|
+
|
|
144
|
+
for (let i = 1; i < changeIndices.length; i++) {
|
|
145
|
+
const idx = changeIndices[i]!;
|
|
146
|
+
if (idx - currentGroup.end <= contextLines * 2 + 1) {
|
|
147
|
+
// Close enough to current group, extend it
|
|
148
|
+
currentGroup.end = idx;
|
|
149
|
+
} else {
|
|
150
|
+
// Start new group
|
|
151
|
+
groups.push(currentGroup);
|
|
152
|
+
currentGroup = { start: idx, end: idx };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
groups.push(currentGroup);
|
|
156
|
+
|
|
157
|
+
// Build result with context
|
|
158
|
+
for (const group of groups) {
|
|
159
|
+
const start = Math.max(0, group.start - contextLines);
|
|
160
|
+
const end = Math.min(diff.length - 1, group.end + contextLines);
|
|
161
|
+
|
|
162
|
+
for (let i = start; i <= end; i++) {
|
|
163
|
+
result.push(diff[i]!);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return result;
|
|
168
|
+
}
|
package/src/sync/skill-files.ts
CHANGED
|
@@ -162,71 +162,36 @@ export async function askForOverwrite(skillIdn: string, existingContent: string,
|
|
|
162
162
|
const gray = '\x1b[90m';
|
|
163
163
|
const reset = '\x1b[0m';
|
|
164
164
|
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
// Find the first differing section and show it with context
|
|
170
|
-
let diffStart = -1;
|
|
171
|
-
let diffEnd = -1;
|
|
172
|
-
|
|
173
|
-
// Find first difference
|
|
174
|
-
for (let i = 0; i < Math.max(localLines.length, remoteLines.length); i++) {
|
|
175
|
-
if (localLines[i] !== remoteLines[i]) {
|
|
176
|
-
diffStart = i;
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
165
|
+
// Generate proper diff using LCS algorithm
|
|
166
|
+
const { generateDiff, filterDiffWithContext } = await import('./diff-utils.js');
|
|
167
|
+
const fullDiff = generateDiff(existingContent, newContent);
|
|
168
|
+
const contextDiff = filterDiffWithContext(fullDiff, 2);
|
|
180
169
|
|
|
181
|
-
if (
|
|
182
|
-
// No differences found (shouldn't happen, but handle gracefully)
|
|
170
|
+
if (contextDiff.length === 0) {
|
|
183
171
|
console.log(`${gray} No differences found${reset}`);
|
|
184
172
|
return 'no';
|
|
185
173
|
}
|
|
186
174
|
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (localLines[i] !== undefined) {
|
|
200
|
-
console.log(` ${String(i + 1).padStart(3)} ${localLines[i]}`);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Show the differences
|
|
205
|
-
const maxDiffLine = Math.min(diffEnd, Math.max(localLines.length, remoteLines.length));
|
|
206
|
-
for (let i = diffStart; i < maxDiffLine; i++) {
|
|
207
|
-
const localLine = localLines[i];
|
|
208
|
-
const remoteLine = remoteLines[i];
|
|
209
|
-
|
|
210
|
-
if (localLine !== undefined && (remoteLine === undefined || localLine !== remoteLine)) {
|
|
211
|
-
console.log(`${redBg} - ${String(i + 1).padStart(3)} ${localLine} ${reset}`);
|
|
212
|
-
}
|
|
213
|
-
if (remoteLine !== undefined && (localLine === undefined || localLine !== remoteLine)) {
|
|
214
|
-
console.log(`${greenBg} + ${String(i + 1).padStart(3)} ${remoteLine} ${reset}`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Show context after (2 lines)
|
|
219
|
-
const contextEnd = Math.min(localLines.length, diffEnd + 2);
|
|
220
|
-
for (let i = diffEnd; i < contextEnd; i++) {
|
|
221
|
-
if (localLines[i] !== undefined) {
|
|
222
|
-
console.log(` ${String(i + 1).padStart(3)} ${localLines[i]}`);
|
|
175
|
+
// Display the diff with proper GitHub-style formatting
|
|
176
|
+
for (const line of contextDiff) {
|
|
177
|
+
if (line.type === 'context') {
|
|
178
|
+
// Show context lines in gray
|
|
179
|
+
const lineNum = line.localLineNum !== -1 ? line.localLineNum : line.remoteLineNum;
|
|
180
|
+
console.log(` ${String(lineNum).padStart(3)} ${line.content}`);
|
|
181
|
+
} else if (line.type === 'remove') {
|
|
182
|
+
// Show local content being removed (red background)
|
|
183
|
+
console.log(`${redBg} - ${String(line.localLineNum).padStart(3)} ${line.content} ${reset}`);
|
|
184
|
+
} else if (line.type === 'add') {
|
|
185
|
+
// Show remote content being added (green background)
|
|
186
|
+
console.log(`${greenBg} + ${String(line.remoteLineNum).padStart(3)} ${line.content} ${reset}`);
|
|
223
187
|
}
|
|
224
188
|
}
|
|
225
189
|
|
|
226
|
-
// Show if there are more
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
|
|
190
|
+
// Show if there are more changes beyond what we're displaying
|
|
191
|
+
const totalChanges = fullDiff.filter(line => line.type !== 'context').length;
|
|
192
|
+
const displayedChanges = contextDiff.filter(line => line.type !== 'context').length;
|
|
193
|
+
if (totalChanges > displayedChanges) {
|
|
194
|
+
console.log(`${gray}... (${totalChanges - displayedChanges} more changes)${reset}`);
|
|
230
195
|
}
|
|
231
196
|
|
|
232
197
|
const answer = await new Promise<string>((resolve) => {
|