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.
@@ -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
@@ -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
- // Show a GitHub-style colored diff with line numbers and context
111
- const localLines = existingContent.trim().split('\n');
112
- const remoteLines = newContent.trim().split('\n');
113
- // Find the first differing section and show it with context
114
- let diffStart = -1;
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
- // Find end of difference section
129
- diffEnd = diffStart;
130
- while (diffEnd < Math.max(localLines.length, remoteLines.length) &&
131
- (localLines[diffEnd] !== remoteLines[diffEnd] ||
132
- localLines[diffEnd] === undefined ||
133
- remoteLines[diffEnd] === undefined)) {
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
- // Show the differences
144
- const maxDiffLine = Math.min(diffEnd, Math.max(localLines.length, remoteLines.length));
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
- // Show context after (2 lines)
156
- const contextEnd = Math.min(localLines.length, diffEnd + 2);
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 differences
163
- const totalDiffs = Math.abs(localLines.length - remoteLines.length) + (diffEnd - diffStart);
164
- if (totalDiffs > (diffEnd - diffStart)) {
165
- console.log(`${gray}... (${totalDiffs - (diffEnd - diffStart)} more differences)${reset}`);
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.5",
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
+ }
@@ -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
- // Show a GitHub-style colored diff with line numbers and context
166
- const localLines = existingContent.trim().split('\n');
167
- const remoteLines = newContent.trim().split('\n');
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 (diffStart === -1) {
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
- // Find end of difference section
188
- diffEnd = diffStart;
189
- while (diffEnd < Math.max(localLines.length, remoteLines.length) &&
190
- (localLines[diffEnd] !== remoteLines[diffEnd] ||
191
- localLines[diffEnd] === undefined ||
192
- remoteLines[diffEnd] === undefined)) {
193
- diffEnd++;
194
- }
195
-
196
- // Show context before (2 lines)
197
- const contextStart = Math.max(0, diffStart - 2);
198
- for (let i = contextStart; i < diffStart; i++) {
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 differences
227
- const totalDiffs = Math.abs(localLines.length - remoteLines.length) + (diffEnd - diffStart);
228
- if (totalDiffs > (diffEnd - diffStart)) {
229
- console.log(`${gray}... (${totalDiffs - (diffEnd - diffStart)} more differences)${reset}`);
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) => {