@compilr-dev/sdk 0.9.5 → 0.9.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.
@@ -19,23 +19,19 @@ export function compressBashOutput(command, stdout) {
19
19
  return compressGitLog(stdout);
20
20
  if (cmd.match(/^git\s+diff/))
21
21
  return compressGitDiff(stdout);
22
- if (cmd.match(/^git\s+(add|commit|push|pull|fetch|merge|rebase|checkout|switch|branch)/))
23
- return compressGitAction(stdout);
22
+ // git write commands (add, commit, push, pull, merge, rebase, checkout, switch, branch)
23
+ // — leave uncompressed. These are side-effect commands that cannot be re-run,
24
+ // and the agent needs to see the full result to confirm what happened.
24
25
  // npm/yarn/pnpm
25
- if (cmd.match(/^(npm|yarn|pnpm)\s+install/))
26
- return compressNpmInstall(stdout);
26
+ // npm install — side-effect command, leave uncompressed
27
27
  if (cmd.match(/^(npm|yarn|pnpm)\s+(test|run\s+test)/))
28
28
  return compressTestOutput(stdout);
29
- if (cmd.match(/^(npm|yarn|pnpm)\s+run\s+(lint|eslint)/))
30
- return compressLintOutput(stdout);
31
- if (cmd.match(/^(npm|yarn|pnpm)\s+run\s+(build|tsc)/))
32
- return compressBuildOutput(stdout);
29
+ // lint output — leave uncompressed. Agent needs every error location to fix code.
30
+ // npm build — side-effect command, leave uncompressed
33
31
  // Direct test runners
34
32
  if (cmd.match(/^(jest|vitest|mocha|pytest|cargo\s+test|go\s+test)/))
35
33
  return compressTestOutput(stdout);
36
- // Direct linters
37
- if (cmd.match(/^(eslint|tsc|ruff|cargo\s+clippy|golangci-lint)/))
38
- return compressLintOutput(stdout);
34
+ // Direct linters — leave uncompressed. Agent needs every error to fix code.
39
35
  // ls (strip metadata, keep filenames)
40
36
  if (cmd.match(/^ls\b/))
41
37
  return compressLsOutput(stdout);
@@ -48,130 +44,181 @@ export function compressBashOutput(command, stdout) {
48
44
  }
49
45
  // ─── Git Status ─────────────────────────────────────────────────────────────
50
46
  function compressGitStatus(output) {
51
- const lines = output.split('\n').filter((l) => l.trim());
52
- if (lines.length <= 20)
53
- return output;
54
- const sections = [];
55
- let current = null;
56
- for (const line of lines) {
57
- if (line.startsWith('On branch') ||
58
- line.startsWith('Your branch') ||
59
- line.startsWith('HEAD detached')) {
60
- sections.push({ header: line, files: [] });
61
- }
62
- else if (line.match(/^(Changes|Untracked|Unmerged)/)) {
63
- current = { header: line, files: [] };
64
- sections.push(current);
65
- }
66
- else if (current &&
67
- (line.startsWith('\t') || line.match(/^\s+(modified|new file|deleted|renamed)/))) {
68
- current.files.push(line.trim());
69
- }
70
- }
71
- // Rebuild with capped file lists
72
- const maxFilesPerSection = 15;
73
47
  const result = [];
74
- for (const section of sections) {
75
- result.push(section.header);
76
- const shown = section.files.slice(0, maxFilesPerSection);
77
- for (const f of shown)
78
- result.push(` ${f}`);
79
- if (section.files.length > maxFilesPerSection) {
80
- result.push(` ... +${String(section.files.length - maxFilesPerSection)} more files`);
48
+ for (const line of output.split('\n')) {
49
+ const trimmed = line.trim();
50
+ // Skip empty lines
51
+ if (!trimmed)
52
+ continue;
53
+ // Skip git hints (noise agent doesn't need instructions on how to use git)
54
+ if (trimmed.startsWith('(use "git') ||
55
+ trimmed.startsWith('(create/copy files') ||
56
+ trimmed.includes('(use "git add') ||
57
+ trimmed.includes('(use "git restore') ||
58
+ trimmed.includes('(use "git checkout') ||
59
+ trimmed.includes('(use "git reset') ||
60
+ trimmed.includes('(use "git pull') ||
61
+ trimmed.includes('(use "git push')) {
62
+ continue;
63
+ }
64
+ // "nothing to commit, working tree clean" — keep and stop
65
+ if (trimmed.includes('nothing to commit') && trimmed.includes('working tree clean')) {
66
+ result.push(trimmed);
67
+ break;
81
68
  }
69
+ result.push(line);
82
70
  }
83
- return result.join('\n');
71
+ return result.length > 0 ? result.join('\n') : output;
84
72
  }
85
73
  // ─── Git Log ────────────────────────────────────────────────────────────────
86
74
  function compressGitLog(output) {
87
75
  const lines = output.split('\n');
88
- if (lines.length <= 30)
76
+ if (lines.length <= 10)
89
77
  return output;
90
- // Keep first 25 lines (recent commits), summarize the rest
91
- const kept = lines.slice(0, 25);
92
- const remaining = lines.slice(25).filter((l) => l.match(/^commit\s/));
93
- if (remaining.length > 0) {
94
- kept.push(`\n... +${String(remaining.length)} older commits omitted`);
78
+ // Condense each commit to one line: hash message (date) <author>
79
+ // Strips verbose Author/Date/body lines, keeps all commits visible.
80
+ // Inspired by RTK's --pretty=format:%h %s (%ar) <%an> approach.
81
+ const result = [];
82
+ let currentHash = '';
83
+ let currentAuthor = '';
84
+ let currentDate = '';
85
+ let currentMessage = '';
86
+ for (const line of lines) {
87
+ const trimmed = line.trim();
88
+ if (line.startsWith('commit ')) {
89
+ // Flush previous commit
90
+ if (currentHash) {
91
+ result.push(formatCommitLine(currentHash, currentMessage, currentDate, currentAuthor));
92
+ }
93
+ currentHash = trimmed.replace('commit ', '').slice(0, 8);
94
+ currentAuthor = '';
95
+ currentDate = '';
96
+ currentMessage = '';
97
+ }
98
+ else if (trimmed.startsWith('Author:')) {
99
+ // Extract just the name (drop email)
100
+ const match = trimmed.match(/Author:\s*(.+?)(?:\s*<.*>)?$/);
101
+ currentAuthor = match ? match[1].trim() : trimmed.replace('Author:', '').trim();
102
+ }
103
+ else if (trimmed.startsWith('Date:')) {
104
+ currentDate = trimmed.replace('Date:', '').trim();
105
+ }
106
+ else if (trimmed &&
107
+ !trimmed.startsWith('Merge:') &&
108
+ !trimmed.startsWith('Signed-off-by:') &&
109
+ !trimmed.startsWith('Co-authored-by:') &&
110
+ !trimmed.startsWith('Co-Authored-By:')) {
111
+ // First non-empty body line = commit message
112
+ if (!currentMessage) {
113
+ currentMessage = trimmed;
114
+ }
115
+ }
95
116
  }
96
- return kept.join('\n');
117
+ // Flush last commit
118
+ if (currentHash) {
119
+ result.push(formatCommitLine(currentHash, currentMessage, currentDate, currentAuthor));
120
+ }
121
+ return result.join('\n');
122
+ }
123
+ function formatCommitLine(hash, message, date, author) {
124
+ // Truncate message to 72 chars
125
+ const msg = message.length > 72 ? message.slice(0, 69) + '...' : message;
126
+ // Shorten date: "Mon Apr 20 12:00:00 2026 +0200" → "Apr 20"
127
+ const shortDate = shortenDate(date);
128
+ return `${hash} ${msg} (${shortDate}) <${author}>`;
129
+ }
130
+ function shortenDate(date) {
131
+ // Try to extract "Mon Day" from various git date formats
132
+ const match = date.match(/(\w{3})\s+(\d{1,2})/);
133
+ if (match)
134
+ return `${match[1]} ${match[2]}`;
135
+ // Fallback: return first 10 chars
136
+ return date.slice(0, 10).trim();
97
137
  }
98
138
  // ─── Git Diff ───────────────────────────────────────────────────────────────
99
139
  function compressGitDiff(output) {
100
140
  const lines = output.split('\n');
101
- if (lines.length <= 50)
141
+ if (lines.length <= 20)
102
142
  return output;
103
- // Keep diff headers and first N lines of each file, collapse large hunks
143
+ // Compact diff inspired by RTK: show file names, hunk headers, +/- lines
144
+ // with per-hunk truncation and per-file +N -N summaries.
104
145
  const result = [];
105
146
  let currentFile = '';
106
- let hunkLines = 0;
107
- const maxHunkLines = 30;
147
+ let added = 0;
148
+ let removed = 0;
149
+ let inHunk = false;
150
+ let hunkShown = 0;
151
+ let hunkSkipped = 0;
152
+ const maxHunkLines = 50;
153
+ const maxTotalLines = 300;
108
154
  for (const line of lines) {
109
155
  if (line.startsWith('diff --git')) {
110
- currentFile = line;
111
- hunkLines = 0;
112
- result.push(line);
156
+ // Flush previous hunk/file
157
+ if (hunkSkipped > 0) {
158
+ result.push(` ... (${String(hunkSkipped)} lines truncated)`);
159
+ hunkSkipped = 0;
160
+ }
161
+ if (currentFile && (added > 0 || removed > 0)) {
162
+ result.push(` +${String(added)} -${String(removed)}`);
163
+ }
164
+ // Start new file — extract filename
165
+ currentFile = line.split(' b/')[1] ?? 'unknown';
166
+ result.push(`\n${currentFile}`);
167
+ added = 0;
168
+ removed = 0;
169
+ inHunk = false;
170
+ hunkShown = 0;
113
171
  }
114
- else if (line.startsWith('---') || line.startsWith('+++') || line.startsWith('@@')) {
115
- hunkLines = 0;
116
- result.push(line);
172
+ else if (line.startsWith('@@')) {
173
+ if (hunkSkipped > 0) {
174
+ result.push(` ... (${String(hunkSkipped)} lines truncated)`);
175
+ hunkSkipped = 0;
176
+ }
177
+ inHunk = true;
178
+ hunkShown = 0;
179
+ result.push(` ${line}`);
117
180
  }
118
- else if (line.startsWith('+') || line.startsWith('-') || line.startsWith(' ')) {
119
- hunkLines++;
120
- if (hunkLines <= maxHunkLines) {
121
- result.push(line);
181
+ else if (inHunk) {
182
+ if (line.startsWith('+') && !line.startsWith('+++')) {
183
+ added++;
184
+ if (hunkShown < maxHunkLines) {
185
+ result.push(` ${line}`);
186
+ hunkShown++;
187
+ }
188
+ else
189
+ hunkSkipped++;
122
190
  }
123
- else if (hunkLines === maxHunkLines + 1) {
124
- result.push(`... (hunk truncated, ${currentFile.split(' b/')[1] ?? 'file'} continues)`);
191
+ else if (line.startsWith('-') && !line.startsWith('---')) {
192
+ removed++;
193
+ if (hunkShown < maxHunkLines) {
194
+ result.push(` ${line}`);
195
+ hunkShown++;
196
+ }
197
+ else
198
+ hunkSkipped++;
199
+ }
200
+ else if (hunkShown < maxHunkLines && !line.startsWith('\\')) {
201
+ if (hunkShown > 0) {
202
+ result.push(` ${line}`);
203
+ hunkShown++;
204
+ }
125
205
  }
126
206
  }
127
- else {
128
- result.push(line);
207
+ if (result.length >= maxTotalLines) {
208
+ result.push('\n... (more changes truncated)');
209
+ break;
129
210
  }
130
211
  }
131
- return result.join('\n');
132
- }
133
- // ─── Git Action (add, commit, push, pull, etc.) ─────────────────────────────
134
- function compressGitAction(output) {
135
- const lines = output.split('\n').filter((l) => l.trim());
136
- if (lines.length <= 15)
137
- return output;
138
- // Keep summary lines, strip verbose file lists
139
- const important = lines.filter((l) => l.match(/^(\[|To |From |Already|Everything|Branch|Updating|Fast-forward|CONFLICT|error:|fatal:|warning:|\s*\d+ file|create mode|delete mode)/) ||
140
- l.includes('->') ||
141
- l.includes('insertions') ||
142
- l.includes('deletions'));
143
- if (important.length > 0 && important.length < lines.length) {
144
- const omitted = lines.length - important.length;
145
- important.push(`(${String(omitted)} lines of detail omitted)`);
146
- return important.join('\n');
212
+ // Flush last file
213
+ if (hunkSkipped > 0) {
214
+ result.push(` ... (${String(hunkSkipped)} lines truncated)`);
147
215
  }
148
- return output;
216
+ if (currentFile && (added > 0 || removed > 0)) {
217
+ result.push(` +${String(added)} -${String(removed)}`);
218
+ }
219
+ return result.join('\n');
149
220
  }
150
221
  // ─── npm install ────────────────────────────────────────────────────────────
151
- function compressNpmInstall(output) {
152
- const lines = output.split('\n');
153
- if (lines.length <= 10)
154
- return output;
155
- // Keep: added/removed/changed summary, warnings, errors
156
- // Strip: progress bars, individual package resolutions, timing
157
- const kept = lines.filter((l) => {
158
- const t = l.trim();
159
- if (!t)
160
- return false;
161
- if (t.startsWith('npm warn') || t.startsWith('npm error') || t.startsWith('npm ERR'))
162
- return true;
163
- if (t.match(/^added \d|^removed \d|^changed \d|^up to date/))
164
- return true;
165
- if (t.includes('vulnerabilit'))
166
- return true;
167
- if (t.startsWith('found '))
168
- return true;
169
- return false;
170
- });
171
- if (kept.length === 0)
172
- return output; // Couldn't parse — return original
173
- return kept.join('\n');
174
- }
175
222
  // ─── Test Output ────────────────────────────────────────────────────────────
176
223
  function compressTestOutput(output) {
177
224
  const lines = output.split('\n');
@@ -217,83 +264,7 @@ function compressTestOutput(output) {
217
264
  return result.join('\n');
218
265
  }
219
266
  // ─── Lint Output ────────────────────────────────────────────────────────────
220
- function compressLintOutput(output) {
221
- const lines = output.split('\n');
222
- if (lines.length <= 15)
223
- return output;
224
- // Group errors by rule, cap per rule
225
- const errors = [];
226
- const warnings = [];
227
- const summaryLines = [];
228
- const ruleCount = new Map();
229
- const maxPerRule = 5;
230
- for (const line of lines) {
231
- const t = line.trim();
232
- // Summary lines
233
- if (t.match(/^✖|^\d+ problem|^\d+ error|^\d+ warning|^error:|^warning:/i)) {
234
- summaryLines.push(line);
235
- continue;
236
- }
237
- // ESLint-style: "file:line:col error message rule"
238
- const ruleMatch = t.match(/\s+(error|warning)\s+.+\s+(\S+)$/);
239
- if (ruleMatch) {
240
- const rule = ruleMatch[2];
241
- const count = (ruleCount.get(rule) ?? 0) + 1;
242
- ruleCount.set(rule, count);
243
- if (count <= maxPerRule) {
244
- if (ruleMatch[1] === 'error')
245
- errors.push(line);
246
- else
247
- warnings.push(line);
248
- }
249
- continue;
250
- }
251
- // TypeScript-style: "file(line,col): error TS..."
252
- if (t.match(/error TS\d+/)) {
253
- errors.push(line);
254
- continue;
255
- }
256
- }
257
- const result = [...errors, ...warnings.slice(0, 20)];
258
- // Show rules that were capped
259
- for (const [rule, count] of ruleCount) {
260
- if (count > maxPerRule) {
261
- result.push(` ... +${String(count - maxPerRule)} more ${rule}`);
262
- }
263
- }
264
- result.push(...summaryLines);
265
- if (result.length < lines.length) {
266
- return result.join('\n');
267
- }
268
- return output;
269
- }
270
267
  // ─── Build Output ───────────────────────────────────────────────────────────
271
- function compressBuildOutput(output) {
272
- const lines = output.split('\n');
273
- if (lines.length <= 20)
274
- return output;
275
- // Keep errors and summary, strip progress/compilation messages
276
- const kept = lines.filter((l) => {
277
- const t = l.trim();
278
- if (!t)
279
- return false;
280
- if (t.match(/^(error|Error|ERROR|warning|Warning|WARN)/))
281
- return true;
282
- if (t.match(/^(✓|✗|✘|Done|Built|Compiled|Successfully|Failed|FAIL)/))
283
- return true;
284
- if (t.match(/error TS\d+/))
285
- return true;
286
- if (t.includes('bundle') && t.includes('kB'))
287
- return true; // Bundle size info
288
- return false;
289
- });
290
- if (kept.length > 0 && kept.length < lines.length * 0.7) {
291
- const omitted = lines.length - kept.length;
292
- kept.push(`(${String(omitted)} lines of build output omitted)`);
293
- return kept.join('\n');
294
- }
295
- return output;
296
- }
297
268
  // ─── ls Output (strip metadata, keep filenames) ────────────────────────────
298
269
  function compressLsOutput(output) {
299
270
  const lines = output.split('\n');
@@ -328,16 +299,19 @@ function compressLsOutput(output) {
328
299
  // ─── curl / wget ────────────────────────────────────────────────────────────
329
300
  function compressCurlOutput(output) {
330
301
  const lines = output.split('\n');
331
- if (lines.length <= 10)
332
- return output;
333
- // Strip progress bars (curl -# output), keep headers and body
302
+ // Strip progress bars and transfer stats, keep headers and body
334
303
  const kept = lines.filter((l) => {
335
304
  const t = l.trim();
336
- // Strip progress indicators
337
- if (t.match(/^[#\s]*\d+(\.\d+)?%/) || t.match(/^\s*\d+\s+\d+\s+\d+\s+\d+/))
305
+ if (!t)
306
+ return true; // Keep blank lines (may separate headers from body)
307
+ // Strip curl progress table header: " % Total % Received ..."
308
+ if (t.startsWith('% Total') || t.startsWith('Dload'))
309
+ return false;
310
+ // Strip progress rows: " 0 0 0 0 0 0..." or " 50 1200 50 600..."
311
+ if (t.match(/^\d+\s+\d+\s+\d+\s+\d+/))
338
312
  return false;
339
- // Strip curl stats
340
- if (t.match(/^\s*(Dload|Upload|Total|Spent|Left|Speed)/))
313
+ // Strip progress percentage lines
314
+ if (t.match(/^[#\s]*\d+(\.\d+)?%/))
341
315
  return false;
342
316
  return true;
343
317
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@compilr-dev/sdk",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
4
4
  "description": "Universal agent runtime for building AI-powered applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",