@compilr-dev/sdk 0.9.5 → 0.9.7
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/compressors/bash.js +164 -190
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/platform/index.d.ts +2 -1
- package/dist/platform/index.js +1 -1
- package/dist/platform/tools/image-tools.d.ts +37 -0
- package/dist/platform/tools/image-tools.js +102 -0
- package/dist/platform/tools/index.d.ts +8 -4
- package/dist/platform/tools/index.js +8 -4
- package/package.json +1 -1
package/dist/compressors/bash.js
CHANGED
|
@@ -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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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 <=
|
|
76
|
+
if (lines.length <= 10)
|
|
89
77
|
return output;
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
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 <=
|
|
141
|
+
if (lines.length <= 20)
|
|
102
142
|
return output;
|
|
103
|
-
//
|
|
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
|
|
107
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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('
|
|
115
|
-
|
|
116
|
-
|
|
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 (
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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 (
|
|
124
|
-
|
|
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
|
-
|
|
128
|
-
result.push(
|
|
207
|
+
if (result.length >= maxTotalLines) {
|
|
208
|
+
result.push('\n... (more changes truncated)');
|
|
209
|
+
break;
|
|
129
210
|
}
|
|
130
211
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
337
|
-
|
|
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
|
|
340
|
-
if (t.match(
|
|
313
|
+
// Strip progress percentage lines
|
|
314
|
+
if (t.match(/^[#\s]*\d+(\.\d+)?%/))
|
|
341
315
|
return false;
|
|
342
316
|
return true;
|
|
343
317
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -58,8 +58,8 @@ export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemReposi
|
|
|
58
58
|
export type { SQLiteRepositories, CreateSQLiteRepositoriesOptions, ProjectDeleteHooks, ProjectRecord, WorkItemRecord, ProjectDocumentRecord, WorkItemCommentRecord, } from './platform/index.js';
|
|
59
59
|
export { createAskUserTool, createAskUserSimpleTool } from './tools/index.js';
|
|
60
60
|
export type { AskUserQuestion, AskUserInput, AskUserResult, AskUserHandler, AskUserSimpleInput, AskUserSimpleResult, AskUserSimpleHandler, } from './tools/index.js';
|
|
61
|
-
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, ProjectAnchorStore, } from './platform/index.js';
|
|
62
|
-
export type { ProjectAnchorStoreConfig } from './platform/index.js';
|
|
61
|
+
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, createImageTools, ProjectAnchorStore, } from './platform/index.js';
|
|
62
|
+
export type { ProjectAnchorStoreConfig, ImageToolsConfig, ImageResizer } from './platform/index.js';
|
|
63
63
|
export { STEP_ORDER, GUIDED_STEP_CRITERIA, getNextStep, isValidTransition, getStepCriteria, formatStepDisplay, getStepNumber, } from './platform/index.js';
|
|
64
64
|
export { platformSkills, designSkill, sketchSkill, prdSkill, refineSkill, refineItemSkill, architectureSkill, sessionNotesSkill, buildSkill, scaffoldSkill, outlineSkill, literatureReviewSkill, draftSectionSkill, peerReviewSkill, researchScaffoldSkill, businessVisionSkill, marketAnalysisSkill, competitorAnalysisSkill, financialModelSkill, pitchOutlineSkill, businessReviewSkill, brandSetupSkill, contentStrategySkill, contentCalendarSkill, createContentSkill, contentReviewSkill, curriculumDesignSkill, lessonPlanSkill, assessmentDesignSkill, courseReviewSkill, bookOutlineSkill, characterDesignSkill, plotThreadsSkill, sceneBreakdownSkill, bookReviewSkill, } from './skills/index.js';
|
|
65
65
|
export { ACTION_REGISTRY, getActionsForContext, getActionById, resolveActionPrompt, buildContextSummary, getSuggestedRole, } from './actions/index.js';
|
package/dist/index.js
CHANGED
|
@@ -131,7 +131,7 @@ export { createAskUserTool, createAskUserSimpleTool } from './tools/index.js';
|
|
|
131
131
|
// =============================================================================
|
|
132
132
|
// Platform Tools (runtime — createPlatformTools factory + individual factories)
|
|
133
133
|
// =============================================================================
|
|
134
|
-
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, ProjectAnchorStore, } from './platform/index.js';
|
|
134
|
+
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, createImageTools, ProjectAnchorStore, } from './platform/index.js';
|
|
135
135
|
// =============================================================================
|
|
136
136
|
// Platform Workflow (pure step-criteria logic)
|
|
137
137
|
// =============================================================================
|
package/dist/platform/index.d.ts
CHANGED
|
@@ -5,7 +5,8 @@ export type { ProjectType, ProjectStatus, RepoPattern, WorkflowMode, LifecycleSt
|
|
|
5
5
|
export type { IProjectRepository, IWorkItemRepository, IDocumentRepository, IPlanRepository, ICommentRepository, } from './repositories.js';
|
|
6
6
|
export type { IAnchorService, IArtifactService, IEpisodeService, AnchorData, ArtifactType, ArtifactData, ArtifactSummaryData, WorkEpisode, ProjectWorkSummary, } from './services.js';
|
|
7
7
|
export type { PlatformContext, PlatformToolsConfig, PlatformHooks } from './context.js';
|
|
8
|
-
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, } from './tools/index.js';
|
|
8
|
+
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, createImageTools, } from './tools/index.js';
|
|
9
|
+
export type { ImageToolsConfig, ImageResizer } from './tools/index.js';
|
|
9
10
|
export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemRepository, SQLiteDocumentRepository, SQLitePlanRepository, SQLiteCommentRepository, getDatabase, closeDatabase, closeAllDatabases, databaseExists, SCHEMA_VERSION, SCHEMA_SQL, } from './sqlite/index.js';
|
|
10
11
|
export type { SQLiteRepositories, CreateSQLiteRepositoriesOptions, ProjectDeleteHooks, ProjectRecord, WorkItemRecord, ProjectDocumentRecord, WorkItemCommentRecord, } from './sqlite/index.js';
|
|
11
12
|
export { ProjectAnchorStore } from './file-anchor-service.js';
|
package/dist/platform/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Platform — Repository interfaces, data models, tools, and workflow.
|
|
3
3
|
*/
|
|
4
4
|
// Platform tools (runtime)
|
|
5
|
-
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, } from './tools/index.js';
|
|
5
|
+
export { createPlatformTools, createProjectTools, createWorkItemTools, createDocumentTools, createPlanTools, createBacklogTools, createAnchorTools, createArtifactTools, createEpisodeTools, createImageTools, } from './tools/index.js';
|
|
6
6
|
// SQLite implementations (concrete repositories)
|
|
7
7
|
export { createSQLiteRepositories, SQLiteProjectRepository, SQLiteWorkItemRepository, SQLiteDocumentRepository, SQLitePlanRepository, SQLiteCommentRepository, getDatabase, closeDatabase, closeAllDatabases, databaseExists, SCHEMA_VERSION, SCHEMA_SQL, } from './sqlite/index.js';
|
|
8
8
|
// File-based anchor service (shared by CLI and Desktop)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Tools — View image files for visual analysis.
|
|
3
|
+
*
|
|
4
|
+
* 1 tool: view_image
|
|
5
|
+
*
|
|
6
|
+
* Returns image data as imageBlocks on ToolExecutionResult, which the agent
|
|
7
|
+
* injects as sibling content blocks alongside the tool_result message.
|
|
8
|
+
* This lets vision-capable LLMs see the image.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Max dimension for resizing. Claude recommends 1568px max.
|
|
12
|
+
* Resizing is optional — only applied if a resizer function is provided.
|
|
13
|
+
*/
|
|
14
|
+
export declare const DEFAULT_MAX_DIMENSION = 1568;
|
|
15
|
+
/**
|
|
16
|
+
* Optional image resizer function. Provided by the host environment
|
|
17
|
+
* (e.g., Electron's nativeImage, sharp, etc.)
|
|
18
|
+
*/
|
|
19
|
+
export interface ImageResizer {
|
|
20
|
+
resize(data: Uint8Array, maxDimension: number): Promise<{
|
|
21
|
+
data: Uint8Array;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
mediaType: string;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
export interface ImageToolsConfig {
|
|
28
|
+
/** Working directory for resolving relative paths */
|
|
29
|
+
cwd?: string;
|
|
30
|
+
/** Optional image resizer (e.g., nativeImage in Electron) */
|
|
31
|
+
resizer?: ImageResizer;
|
|
32
|
+
/** Max dimension for resize (default: 1568) */
|
|
33
|
+
maxDimension?: number;
|
|
34
|
+
}
|
|
35
|
+
export declare function createImageTools(config?: ImageToolsConfig): readonly [import("@compilr-dev/agents").Tool<{
|
|
36
|
+
path: string;
|
|
37
|
+
}>];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image Tools — View image files for visual analysis.
|
|
3
|
+
*
|
|
4
|
+
* 1 tool: view_image
|
|
5
|
+
*
|
|
6
|
+
* Returns image data as imageBlocks on ToolExecutionResult, which the agent
|
|
7
|
+
* injects as sibling content blocks alongside the tool_result message.
|
|
8
|
+
* This lets vision-capable LLMs see the image.
|
|
9
|
+
*/
|
|
10
|
+
import * as fs from 'node:fs';
|
|
11
|
+
import * as path from 'node:path';
|
|
12
|
+
import { defineTool, createErrorResult } from '@compilr-dev/agents';
|
|
13
|
+
/** Supported image MIME types */
|
|
14
|
+
const IMAGE_EXTENSIONS = {
|
|
15
|
+
'.png': 'image/png',
|
|
16
|
+
'.jpg': 'image/jpeg',
|
|
17
|
+
'.jpeg': 'image/jpeg',
|
|
18
|
+
'.gif': 'image/gif',
|
|
19
|
+
'.webp': 'image/webp',
|
|
20
|
+
};
|
|
21
|
+
/** Max file size: 10MB (base64 will be ~13MB, well within API limits) */
|
|
22
|
+
const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
23
|
+
/**
|
|
24
|
+
* Max dimension for resizing. Claude recommends 1568px max.
|
|
25
|
+
* Resizing is optional — only applied if a resizer function is provided.
|
|
26
|
+
*/
|
|
27
|
+
export const DEFAULT_MAX_DIMENSION = 1568;
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
29
|
+
export function createImageTools(config = {}) {
|
|
30
|
+
const cwd = config.cwd ?? process.cwd();
|
|
31
|
+
const maxDimension = config.maxDimension ?? DEFAULT_MAX_DIMENSION;
|
|
32
|
+
const viewImageTool = defineTool({
|
|
33
|
+
name: 'view_image',
|
|
34
|
+
description: 'View an image file for visual analysis. Returns the image so you can see it. ' +
|
|
35
|
+
'Supports PNG, JPEG, GIF, and WebP formats.',
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
path: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'Path to the image file (absolute or relative to working directory)',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ['path'],
|
|
45
|
+
},
|
|
46
|
+
execute: async (input) => {
|
|
47
|
+
// Resolve path
|
|
48
|
+
const filePath = path.isAbsolute(input.path) ? input.path : path.resolve(cwd, input.path);
|
|
49
|
+
// Validate file exists
|
|
50
|
+
if (!fs.existsSync(filePath)) {
|
|
51
|
+
return createErrorResult(`File not found: ${input.path}`);
|
|
52
|
+
}
|
|
53
|
+
// Validate extension
|
|
54
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
55
|
+
const mediaType = IMAGE_EXTENSIONS[ext];
|
|
56
|
+
if (!mediaType) {
|
|
57
|
+
const supported = Object.keys(IMAGE_EXTENSIONS).join(', ');
|
|
58
|
+
return createErrorResult(`Unsupported image format: ${ext}. Supported formats: ${supported}`);
|
|
59
|
+
}
|
|
60
|
+
// Check file size
|
|
61
|
+
const stat = fs.statSync(filePath);
|
|
62
|
+
if (stat.size > MAX_FILE_SIZE) {
|
|
63
|
+
const sizeMB = (stat.size / (1024 * 1024)).toFixed(1);
|
|
64
|
+
return createErrorResult(`Image too large: ${sizeMB}MB (max 10MB)`);
|
|
65
|
+
}
|
|
66
|
+
// Read file
|
|
67
|
+
let imageData = fs.readFileSync(filePath);
|
|
68
|
+
let finalMediaType = mediaType;
|
|
69
|
+
let width;
|
|
70
|
+
let height;
|
|
71
|
+
// Optional resize
|
|
72
|
+
if (config.resizer) {
|
|
73
|
+
try {
|
|
74
|
+
const resized = await config.resizer.resize(imageData, maxDimension);
|
|
75
|
+
imageData = resized.data;
|
|
76
|
+
width = resized.width;
|
|
77
|
+
height = resized.height;
|
|
78
|
+
finalMediaType = resized.mediaType;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Resize failed — use original data
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const base64 = Buffer.from(imageData).toString('base64');
|
|
85
|
+
const filename = path.basename(filePath);
|
|
86
|
+
return {
|
|
87
|
+
success: true,
|
|
88
|
+
result: `Image loaded: ${filename} (${finalMediaType})`,
|
|
89
|
+
imageBlocks: [
|
|
90
|
+
{
|
|
91
|
+
data: base64,
|
|
92
|
+
mediaType: finalMediaType,
|
|
93
|
+
filename,
|
|
94
|
+
width,
|
|
95
|
+
height,
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
return [viewImageTool];
|
|
102
|
+
}
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform Tools — Factory function for all platform tools.
|
|
3
3
|
*
|
|
4
|
-
* Returns up to
|
|
4
|
+
* Returns up to 34 tool definitions operating against async PlatformContext.
|
|
5
5
|
* - 25 DB tools (always): project, workitem (incl. comment), document, plan, backlog
|
|
6
|
+
* - 1 image tool (always): view_image
|
|
6
7
|
* - 3 anchor tools (if ctx.anchors provided)
|
|
7
8
|
* - 4 artifact tools (if ctx.artifacts provided)
|
|
8
9
|
* - 1 episode tool (if ctx.episodes provided)
|
|
9
10
|
*/
|
|
10
11
|
import type { Tool } from '@compilr-dev/agents';
|
|
11
12
|
import type { PlatformToolsConfig } from '../context.js';
|
|
13
|
+
import type { ImageToolsConfig } from './image-tools.js';
|
|
12
14
|
/**
|
|
13
15
|
* Create all platform tools operating against the given context.
|
|
14
|
-
* Returns
|
|
15
|
-
* if the optional anchors/artifacts/episodes services are provided.
|
|
16
|
+
* Returns 25 DB tools + 1 image tool unconditionally, plus up to 8 service-based
|
|
17
|
+
* tools if the optional anchors/artifacts/episodes services are provided.
|
|
16
18
|
*/
|
|
17
|
-
export declare function createPlatformTools(config: PlatformToolsConfig): Tool<never>[];
|
|
19
|
+
export declare function createPlatformTools(config: PlatformToolsConfig, imageConfig?: ImageToolsConfig): Tool<never>[];
|
|
18
20
|
export { createProjectTools } from './project-tools.js';
|
|
19
21
|
export { createWorkItemTools } from './workitem-tools.js';
|
|
20
22
|
export { createDocumentTools } from './document-tools.js';
|
|
@@ -23,3 +25,5 @@ export { createBacklogTools } from './backlog-tools.js';
|
|
|
23
25
|
export { createAnchorTools } from './anchor-tools.js';
|
|
24
26
|
export { createArtifactTools } from './artifact-tools.js';
|
|
25
27
|
export { createEpisodeTools } from './episode-tools.js';
|
|
28
|
+
export { createImageTools } from './image-tools.js';
|
|
29
|
+
export type { ImageToolsConfig, ImageResizer } from './image-tools.js';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform Tools — Factory function for all platform tools.
|
|
3
3
|
*
|
|
4
|
-
* Returns up to
|
|
4
|
+
* Returns up to 34 tool definitions operating against async PlatformContext.
|
|
5
5
|
* - 25 DB tools (always): project, workitem (incl. comment), document, plan, backlog
|
|
6
|
+
* - 1 image tool (always): view_image
|
|
6
7
|
* - 3 anchor tools (if ctx.anchors provided)
|
|
7
8
|
* - 4 artifact tools (if ctx.artifacts provided)
|
|
8
9
|
* - 1 episode tool (if ctx.episodes provided)
|
|
@@ -15,13 +16,14 @@ import { createBacklogTools } from './backlog-tools.js';
|
|
|
15
16
|
import { createAnchorTools } from './anchor-tools.js';
|
|
16
17
|
import { createArtifactTools } from './artifact-tools.js';
|
|
17
18
|
import { createEpisodeTools } from './episode-tools.js';
|
|
19
|
+
import { createImageTools } from './image-tools.js';
|
|
18
20
|
/**
|
|
19
21
|
* Create all platform tools operating against the given context.
|
|
20
|
-
* Returns
|
|
21
|
-
* if the optional anchors/artifacts/episodes services are provided.
|
|
22
|
+
* Returns 25 DB tools + 1 image tool unconditionally, plus up to 8 service-based
|
|
23
|
+
* tools if the optional anchors/artifacts/episodes services are provided.
|
|
22
24
|
*/
|
|
23
25
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
24
|
-
export function createPlatformTools(config) {
|
|
26
|
+
export function createPlatformTools(config, imageConfig) {
|
|
25
27
|
// Use Tool<never>[] to accept any Tool<T> — the generic parameter is only
|
|
26
28
|
// relevant to execute() callers, not to the registry that stores them.
|
|
27
29
|
const tools = [
|
|
@@ -30,6 +32,7 @@ export function createPlatformTools(config) {
|
|
|
30
32
|
...createDocumentTools(config),
|
|
31
33
|
...createPlanTools(config),
|
|
32
34
|
...createBacklogTools(config),
|
|
35
|
+
...createImageTools({ cwd: config.cwd, ...imageConfig }),
|
|
33
36
|
];
|
|
34
37
|
if (config.context.anchors) {
|
|
35
38
|
tools.push(...createAnchorTools(config));
|
|
@@ -51,3 +54,4 @@ export { createBacklogTools } from './backlog-tools.js';
|
|
|
51
54
|
export { createAnchorTools } from './anchor-tools.js';
|
|
52
55
|
export { createArtifactTools } from './artifact-tools.js';
|
|
53
56
|
export { createEpisodeTools } from './episode-tools.js';
|
|
57
|
+
export { createImageTools } from './image-tools.js';
|