newo 2.0.4 → 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 +24 -49
- package/package.json +1 -1
- package/src/sync/diff-utils.ts +168 -0
- package/src/sync/skill-files.ts +25 -56
|
@@ -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,60 +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
|
-
|
|
116
|
-
|
|
117
|
-
let remoteIdx = 0;
|
|
118
|
-
while (localIdx < localLines.length || remoteIdx < remoteLines.length) {
|
|
119
|
-
const localLine = localLines[localIdx];
|
|
120
|
-
const remoteLine = remoteLines[remoteIdx];
|
|
121
|
-
if (localLine !== remoteLine) {
|
|
122
|
-
if (localLine !== undefined && remoteLine !== undefined) {
|
|
123
|
-
diffs.push({ localIndex: localIdx, remoteIndex: remoteIdx, type: 'change' });
|
|
124
|
-
}
|
|
125
|
-
else if (localLine !== undefined) {
|
|
126
|
-
diffs.push({ localIndex: localIdx, remoteIndex: -1, type: 'remove' });
|
|
127
|
-
}
|
|
128
|
-
else if (remoteLine !== undefined) {
|
|
129
|
-
diffs.push({ localIndex: -1, remoteIndex: remoteIdx, type: 'add' });
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
if (localLine !== undefined)
|
|
133
|
-
localIdx++;
|
|
134
|
-
if (remoteLine !== undefined)
|
|
135
|
-
remoteIdx++;
|
|
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) {
|
|
115
|
+
console.log(`${gray} No differences found${reset}`);
|
|
116
|
+
return 'no';
|
|
136
117
|
}
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
console.log(
|
|
143
|
-
// Show context before
|
|
144
|
-
const contextStart = Math.max(0, diff.localIndex - 2);
|
|
145
|
-
for (let i = contextStart; i < diff.localIndex && i < localLines.length; i++) {
|
|
146
|
-
console.log(` ${String(i + 1).padStart(3)} ${localLines[i]}`);
|
|
147
|
-
}
|
|
148
|
-
// Show the actual diff
|
|
149
|
-
if (diff.type === 'change' || diff.type === 'remove') {
|
|
150
|
-
console.log(`${redBg} - ${String(diff.localIndex + 1).padStart(3)} ${localLines[diff.localIndex]} ${reset}`);
|
|
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}`);
|
|
151
124
|
}
|
|
152
|
-
if (
|
|
153
|
-
|
|
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}`);
|
|
154
128
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
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}`);
|
|
159
132
|
}
|
|
160
|
-
shown++;
|
|
161
133
|
}
|
|
162
|
-
if
|
|
163
|
-
|
|
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}`);
|
|
164
139
|
}
|
|
165
140
|
const answer = await new Promise((resolve) => {
|
|
166
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,67 +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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
let localIdx = 0;
|
|
174
|
-
let remoteIdx = 0;
|
|
175
|
-
|
|
176
|
-
while (localIdx < localLines.length || remoteIdx < remoteLines.length) {
|
|
177
|
-
const localLine = localLines[localIdx];
|
|
178
|
-
const remoteLine = remoteLines[remoteIdx];
|
|
179
|
-
|
|
180
|
-
if (localLine !== remoteLine) {
|
|
181
|
-
if (localLine !== undefined && remoteLine !== undefined) {
|
|
182
|
-
diffs.push({localIndex: localIdx, remoteIndex: remoteIdx, type: 'change'});
|
|
183
|
-
} else if (localLine !== undefined) {
|
|
184
|
-
diffs.push({localIndex: localIdx, remoteIndex: -1, type: 'remove'});
|
|
185
|
-
} else if (remoteLine !== undefined) {
|
|
186
|
-
diffs.push({localIndex: -1, remoteIndex: remoteIdx, type: 'add'});
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (localLine !== undefined) localIdx++;
|
|
191
|
-
if (remoteLine !== undefined) remoteIdx++;
|
|
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);
|
|
169
|
+
|
|
170
|
+
if (contextDiff.length === 0) {
|
|
171
|
+
console.log(`${gray} No differences found${reset}`);
|
|
172
|
+
return 'no';
|
|
192
173
|
}
|
|
193
174
|
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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}`);
|
|
205
187
|
}
|
|
206
|
-
|
|
207
|
-
// Show the actual diff
|
|
208
|
-
if (diff.type === 'change' || diff.type === 'remove') {
|
|
209
|
-
console.log(`${redBg} - ${String(diff.localIndex + 1).padStart(3)} ${localLines[diff.localIndex]} ${reset}`);
|
|
210
|
-
}
|
|
211
|
-
if (diff.type === 'change' || diff.type === 'add') {
|
|
212
|
-
console.log(`${greenBg} + ${String(diff.remoteIndex + 1).padStart(3)} ${remoteLines[diff.remoteIndex]} ${reset}`);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// Show context after
|
|
216
|
-
const contextEnd = Math.min(localLines.length, diff.localIndex + 3);
|
|
217
|
-
for (let i = diff.localIndex + 1; i < contextEnd; i++) {
|
|
218
|
-
console.log(` ${String(i + 1).padStart(3)} ${localLines[i]}`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
shown++;
|
|
222
188
|
}
|
|
223
189
|
|
|
224
|
-
if
|
|
225
|
-
|
|
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}`);
|
|
226
195
|
}
|
|
227
196
|
|
|
228
197
|
const answer = await new Promise<string>((resolve) => {
|