@link-assistant/hive-mind 0.46.1 → 0.47.0
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/CHANGELOG.md +10 -15
- package/README.md +42 -8
- package/package.json +16 -3
- package/src/agent.lib.mjs +49 -70
- package/src/agent.prompts.lib.mjs +6 -20
- package/src/buildUserMention.lib.mjs +4 -17
- package/src/claude-limits.lib.mjs +15 -15
- package/src/claude.lib.mjs +617 -626
- package/src/claude.prompts.lib.mjs +7 -22
- package/src/codex.lib.mjs +39 -71
- package/src/codex.prompts.lib.mjs +6 -20
- package/src/config.lib.mjs +3 -16
- package/src/contributing-guidelines.lib.mjs +5 -18
- package/src/exit-handler.lib.mjs +4 -4
- package/src/git.lib.mjs +7 -7
- package/src/github-issue-creator.lib.mjs +17 -17
- package/src/github-linking.lib.mjs +8 -33
- package/src/github.batch.lib.mjs +20 -16
- package/src/github.graphql.lib.mjs +18 -18
- package/src/github.lib.mjs +89 -91
- package/src/hive.config.lib.mjs +50 -50
- package/src/hive.mjs +1293 -1296
- package/src/instrument.mjs +7 -11
- package/src/interactive-mode.lib.mjs +112 -138
- package/src/lenv-reader.lib.mjs +1 -6
- package/src/lib.mjs +36 -45
- package/src/lino.lib.mjs +2 -2
- package/src/local-ci-checks.lib.mjs +15 -14
- package/src/memory-check.mjs +52 -60
- package/src/model-mapping.lib.mjs +25 -32
- package/src/model-validation.lib.mjs +31 -31
- package/src/opencode.lib.mjs +37 -62
- package/src/opencode.prompts.lib.mjs +7 -21
- package/src/protect-branch.mjs +14 -15
- package/src/review.mjs +28 -27
- package/src/reviewers-hive.mjs +64 -69
- package/src/sentry.lib.mjs +13 -10
- package/src/solve.auto-continue.lib.mjs +48 -38
- package/src/solve.auto-pr.lib.mjs +111 -69
- package/src/solve.branch-errors.lib.mjs +17 -46
- package/src/solve.branch.lib.mjs +16 -23
- package/src/solve.config.lib.mjs +263 -261
- package/src/solve.error-handlers.lib.mjs +21 -79
- package/src/solve.execution.lib.mjs +10 -18
- package/src/solve.feedback.lib.mjs +25 -46
- package/src/solve.mjs +59 -60
- package/src/solve.preparation.lib.mjs +10 -36
- package/src/solve.repo-setup.lib.mjs +4 -19
- package/src/solve.repository.lib.mjs +37 -37
- package/src/solve.results.lib.mjs +32 -46
- package/src/solve.session.lib.mjs +7 -22
- package/src/solve.validation.lib.mjs +19 -17
- package/src/solve.watch.lib.mjs +20 -33
- package/src/start-screen.mjs +24 -24
- package/src/task.mjs +38 -44
- package/src/telegram-bot.mjs +125 -121
- package/src/telegram-top-command.lib.mjs +32 -48
- package/src/usage-limit.lib.mjs +9 -13
- package/src/version-info.lib.mjs +1 -1
- package/src/version.lib.mjs +1 -1
- package/src/youtrack/solve.youtrack.lib.mjs +3 -8
- package/src/youtrack/youtrack-sync.mjs +8 -14
- package/src/youtrack/youtrack.lib.mjs +26 -28
package/src/memory-check.mjs
CHANGED
|
@@ -29,10 +29,10 @@ const { log: libLog, setLogFile } = lib;
|
|
|
29
29
|
// Function to check available disk space
|
|
30
30
|
export const checkDiskSpace = async (minSpaceMB = 500, options = {}) => {
|
|
31
31
|
const log = options.log || libLog;
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
try {
|
|
34
34
|
let availableMB;
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
if (process.platform === 'darwin') {
|
|
37
37
|
// macOS: use df -m (megabytes) and get the 4th column
|
|
38
38
|
const { stdout } = await $silent`df -m . 2>/dev/null | tail -1 | awk '{print $4}'`;
|
|
@@ -42,23 +42,23 @@ export const checkDiskSpace = async (minSpaceMB = 500, options = {}) => {
|
|
|
42
42
|
const { stdout } = await $silent`powershell -Command "(Get-PSDrive -Name (Get-Location).Drive.Name).Free / 1MB"`;
|
|
43
43
|
availableMB = Math.floor(parseFloat(stdout.toString().trim()));
|
|
44
44
|
} else {
|
|
45
|
-
// Linux: use df -BM and get the 4th column
|
|
45
|
+
// Linux: use df -BM and get the 4th column
|
|
46
46
|
const { stdout } = await $silent`df -BM . 2>/dev/null | tail -1 | awk '{print $4}'`;
|
|
47
47
|
availableMB = parseInt(stdout.toString().replace('M', ''));
|
|
48
48
|
}
|
|
49
|
-
|
|
49
|
+
|
|
50
50
|
if (isNaN(availableMB)) {
|
|
51
51
|
await log('❌ Failed to parse disk space information');
|
|
52
52
|
return { success: false, availableMB: 0, error: 'Failed to parse disk space' };
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
if (availableMB < minSpaceMB) {
|
|
56
56
|
await log(`❌ Insufficient disk space: ${availableMB}MB available, ${minSpaceMB}MB required`);
|
|
57
57
|
await log(' This may prevent successful operations.');
|
|
58
58
|
await log(' Please free up disk space and try again.');
|
|
59
59
|
return { success: false, availableMB, required: minSpaceMB };
|
|
60
60
|
}
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
await log(`💾 Disk space check: ${availableMB}MB available (${minSpaceMB}MB required) ✅`);
|
|
63
63
|
return { success: true, availableMB, required: minSpaceMB };
|
|
64
64
|
} catch (error) {
|
|
@@ -70,47 +70,47 @@ export const checkDiskSpace = async (minSpaceMB = 500, options = {}) => {
|
|
|
70
70
|
// Function to check available RAM (volatile memory)
|
|
71
71
|
export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
72
72
|
const log = options.log || libLog;
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
// Check platform first
|
|
75
75
|
if (process.platform === 'darwin') {
|
|
76
76
|
// macOS RAM check using vm_stat
|
|
77
77
|
try {
|
|
78
78
|
const { stdout: vmStatOutput } = await $silent`vm_stat 2>/dev/null`;
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
// Parse page size
|
|
81
81
|
const pageSizeMatch = vmStatOutput.toString().match(/page size of (\d+) bytes/);
|
|
82
82
|
const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1]) : 16384; // Default to 16KB
|
|
83
|
-
|
|
83
|
+
|
|
84
84
|
// Parse free pages (handle dots in numbers like "6009.")
|
|
85
85
|
const freeMatch = vmStatOutput.toString().match(/Pages free:\s+([\d.]+)/);
|
|
86
86
|
const freePages = freeMatch ? parseInt(freeMatch[1].replace(/\./g, '')) : 0;
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
// Parse inactive pages (can be reclaimed)
|
|
89
89
|
const inactiveMatch = vmStatOutput.toString().match(/Pages inactive:\s+([\d.]+)/);
|
|
90
90
|
const inactivePages = inactiveMatch ? parseInt(inactiveMatch[1].replace(/\./g, '')) : 0;
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
// Parse purgeable pages (can be freed)
|
|
93
93
|
const purgeableMatch = vmStatOutput.toString().match(/Pages purgeable:\s+([\d.]+)/);
|
|
94
94
|
const purgeablePages = purgeableMatch ? parseInt(purgeableMatch[1].replace(/\./g, '')) : 0;
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
// Calculate available memory (free + inactive + purgeable)
|
|
97
97
|
const availablePages = freePages + inactivePages + purgeablePages;
|
|
98
98
|
const availableBytes = availablePages * pageSize;
|
|
99
99
|
const availableMB = Math.floor(availableBytes / (1024 * 1024));
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
// Check swap status
|
|
102
102
|
const { stdout: swapEnabledOutput } = await $silent`sysctl vm.swap_enabled 2>/dev/null`;
|
|
103
103
|
const swapEnabled = swapEnabledOutput.toString().includes('1');
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
// Get swap usage details
|
|
106
106
|
const { stdout: swapUsageOutput } = await $silent`sysctl vm.swapusage 2>/dev/null`;
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
// Parse swap info
|
|
109
109
|
const swapMatch = swapUsageOutput.toString().match(/total = ([\d.]+)M\s+used = ([\d.]+)M/);
|
|
110
110
|
const swapTotal = swapMatch ? parseFloat(swapMatch[1]) : 0;
|
|
111
111
|
const swapUsed = swapMatch ? parseFloat(swapMatch[2]) : 0;
|
|
112
112
|
const swapAvailable = swapTotal - swapUsed;
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
let swapInfo;
|
|
115
115
|
if (swapEnabled) {
|
|
116
116
|
if (swapTotal > 0) {
|
|
@@ -121,7 +121,7 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
121
121
|
} else {
|
|
122
122
|
swapInfo = 'disabled';
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
|
|
125
125
|
// Calculate total available memory (RAM + swap)
|
|
126
126
|
const totalAvailable = availableMB + (swapEnabled && swapTotal > 0 ? Math.round(swapAvailable) : 0);
|
|
127
127
|
|
|
@@ -141,7 +141,6 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
141
141
|
|
|
142
142
|
await log(`🧠 Memory check: ${availableMB}MB available, swap: ${swapInfo}, total: ${totalAvailable}MB (${minMemoryMB}MB required) ✅`);
|
|
143
143
|
return { success: true, availableMB, required: minMemoryMB, swap: swapInfo, totalAvailable };
|
|
144
|
-
|
|
145
144
|
} catch (error) {
|
|
146
145
|
await log(`❌ macOS memory check failed: ${error.message}`);
|
|
147
146
|
return { success: false, availableMB: 0, error: error.message };
|
|
@@ -150,20 +149,20 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
150
149
|
// Windows memory check using PowerShell
|
|
151
150
|
try {
|
|
152
151
|
const { stdout: memOutput } = await $silent`powershell -Command "Get-CimInstance Win32_OperatingSystem | Select-Object @{Name='AvailableMB';Expression={[math]::Round($_.FreePhysicalMemory/1024)}}, @{Name='TotalPageFileMB';Expression={[math]::Round($_.TotalVirtualMemorySize/1024)}}, @{Name='FreePageFileMB';Expression={[math]::Round($_.FreeVirtualMemory/1024)}} | ConvertTo-Json"`;
|
|
153
|
-
|
|
152
|
+
|
|
154
153
|
const memInfo = JSON.parse(memOutput.toString());
|
|
155
154
|
const availableMB = memInfo.AvailableMB;
|
|
156
155
|
const pageFileTotalMB = memInfo.TotalPageFileMB || 0;
|
|
157
156
|
const pageFileFreeMB = memInfo.FreePageFileMB || 0;
|
|
158
157
|
const pageFileUsedMB = pageFileTotalMB - pageFileFreeMB;
|
|
159
|
-
|
|
158
|
+
|
|
160
159
|
let swapInfo;
|
|
161
160
|
if (pageFileTotalMB > 0) {
|
|
162
161
|
swapInfo = `${pageFileTotalMB}MB (${pageFileUsedMB}MB used)`;
|
|
163
162
|
} else {
|
|
164
163
|
swapInfo = 'none';
|
|
165
164
|
}
|
|
166
|
-
|
|
165
|
+
|
|
167
166
|
// Calculate total available memory (RAM + page file)
|
|
168
167
|
const totalAvailable = availableMB + (pageFileFreeMB || 0);
|
|
169
168
|
|
|
@@ -178,7 +177,6 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
178
177
|
|
|
179
178
|
await log(`🧠 Memory check: ${availableMB}MB available, page file: ${swapInfo}, total: ${totalAvailable}MB (${minMemoryMB}MB required) ✅`);
|
|
180
179
|
return { success: true, availableMB, required: minMemoryMB, swap: swapInfo, totalAvailable };
|
|
181
|
-
|
|
182
180
|
} catch (error) {
|
|
183
181
|
await log(`❌ Windows memory check failed: ${error.message}`);
|
|
184
182
|
return { success: false, availableMB: 0, error: error.message };
|
|
@@ -188,14 +186,14 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
188
186
|
try {
|
|
189
187
|
const meminfoContent = await fs.readFile('/proc/meminfo', 'utf8');
|
|
190
188
|
const lines = meminfoContent.split('\n');
|
|
191
|
-
|
|
192
|
-
const getValue =
|
|
189
|
+
|
|
190
|
+
const getValue = key => {
|
|
193
191
|
const line = lines.find(l => l.startsWith(key));
|
|
194
192
|
if (!line) return 0;
|
|
195
193
|
const match = line.match(/(\d+)/);
|
|
196
194
|
return match ? parseInt(match[1]) : 0;
|
|
197
195
|
};
|
|
198
|
-
|
|
196
|
+
|
|
199
197
|
// Get memory values in KB
|
|
200
198
|
const memFree = getValue('MemFree:');
|
|
201
199
|
const buffers = getValue('Buffers:');
|
|
@@ -203,24 +201,24 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
203
201
|
const sReclaimable = getValue('SReclaimable:');
|
|
204
202
|
const swapTotal = getValue('SwapTotal:');
|
|
205
203
|
const swapFree = getValue('SwapFree:');
|
|
206
|
-
|
|
204
|
+
|
|
207
205
|
// Calculate available memory (similar to 'free' command)
|
|
208
206
|
const availableKB = memFree + buffers + cached + sReclaimable;
|
|
209
207
|
const availableMB = Math.floor(availableKB / 1024);
|
|
210
|
-
|
|
208
|
+
|
|
211
209
|
// Calculate swap info
|
|
212
210
|
const swapUsedKB = swapTotal - swapFree;
|
|
213
211
|
const swapMB = Math.floor(swapTotal / 1024);
|
|
214
212
|
const swapUsedMB = Math.floor(swapUsedKB / 1024);
|
|
215
213
|
const swapAvailableMB = Math.floor(swapFree / 1024);
|
|
216
|
-
|
|
214
|
+
|
|
217
215
|
let swapInfo;
|
|
218
216
|
if (swapTotal > 0) {
|
|
219
217
|
swapInfo = `${swapMB}MB (${swapUsedMB}MB used)`;
|
|
220
218
|
} else {
|
|
221
219
|
swapInfo = 'none';
|
|
222
220
|
}
|
|
223
|
-
|
|
221
|
+
|
|
224
222
|
// Calculate total available memory (RAM + swap)
|
|
225
223
|
const totalAvailable = availableMB + swapAvailableMB;
|
|
226
224
|
|
|
@@ -244,7 +242,6 @@ export const checkRAM = async (minMemoryMB = 256, options = {}) => {
|
|
|
244
242
|
|
|
245
243
|
await log(`🧠 Memory check: ${availableMB}MB available, swap: ${swapInfo}, total: ${totalAvailable}MB (${minMemoryMB}MB required) ✅`);
|
|
246
244
|
return { success: true, availableMB, required: minMemoryMB, swap: swapInfo, totalAvailable };
|
|
247
|
-
|
|
248
245
|
} catch (error) {
|
|
249
246
|
await log(`❌ Linux memory check failed: ${error.message}`);
|
|
250
247
|
return { success: false, availableMB: 0, error: error.message };
|
|
@@ -263,49 +260,45 @@ export const getResourceSnapshot = async () => {
|
|
|
263
260
|
const vmStat = await $silent`vm_stat 2>/dev/null | head -10`;
|
|
264
261
|
const uptime = await $silent`uptime 2>/dev/null`;
|
|
265
262
|
const swap = await $silent`sysctl vm.swapusage 2>/dev/null`;
|
|
266
|
-
|
|
263
|
+
|
|
267
264
|
return {
|
|
268
265
|
timestamp: new Date().toISOString(),
|
|
269
266
|
memory: vmStat.stdout.toString().trim(),
|
|
270
267
|
swap: swap.stdout.toString().trim(),
|
|
271
|
-
uptime: uptime.stdout.toString().trim()
|
|
268
|
+
uptime: uptime.stdout.toString().trim(),
|
|
272
269
|
};
|
|
273
270
|
} else {
|
|
274
271
|
// Linux resource snapshot
|
|
275
272
|
const memInfo = await $silent`grep -E "MemTotal|MemAvailable|MemFree|SwapTotal|SwapFree" /proc/meminfo 2>/dev/null`;
|
|
276
273
|
const loadAvg = await $silent`cat /proc/loadavg`;
|
|
277
274
|
const uptime = await $silent`uptime`;
|
|
278
|
-
|
|
275
|
+
|
|
279
276
|
return {
|
|
280
277
|
timestamp: new Date().toISOString(),
|
|
281
278
|
memory: memInfo.stdout.toString().trim(),
|
|
282
279
|
load: loadAvg.stdout.toString().trim(),
|
|
283
|
-
uptime: uptime.stdout.toString().trim()
|
|
280
|
+
uptime: uptime.stdout.toString().trim(),
|
|
284
281
|
};
|
|
285
282
|
}
|
|
286
283
|
} catch (error) {
|
|
287
284
|
return {
|
|
288
285
|
timestamp: new Date().toISOString(),
|
|
289
|
-
error: `Failed to get resource snapshot: ${error.message}
|
|
286
|
+
error: `Failed to get resource snapshot: ${error.message}`,
|
|
290
287
|
};
|
|
291
288
|
}
|
|
292
289
|
};
|
|
293
290
|
|
|
294
291
|
// Combined system check function
|
|
295
292
|
export const checkSystem = async (requirements = {}, options = {}) => {
|
|
296
|
-
const {
|
|
297
|
-
minMemoryMB = 256,
|
|
298
|
-
minDiskSpaceMB = 500,
|
|
299
|
-
exitOnFailure = false
|
|
300
|
-
} = requirements;
|
|
293
|
+
const { minMemoryMB = 256, minDiskSpaceMB = 500, exitOnFailure = false } = requirements;
|
|
301
294
|
|
|
302
295
|
// Note: log is passed through options to checkDiskSpace and checkRAM
|
|
303
296
|
const results = {
|
|
304
297
|
ram: null,
|
|
305
298
|
disk: null,
|
|
306
|
-
success: true
|
|
299
|
+
success: true,
|
|
307
300
|
};
|
|
308
|
-
|
|
301
|
+
|
|
309
302
|
// Check disk space (persistent memory)
|
|
310
303
|
results.disk = await checkDiskSpace(minDiskSpaceMB, options);
|
|
311
304
|
if (!results.disk.success) {
|
|
@@ -314,7 +307,7 @@ export const checkSystem = async (requirements = {}, options = {}) => {
|
|
|
314
307
|
process.exit(1);
|
|
315
308
|
}
|
|
316
309
|
}
|
|
317
|
-
|
|
310
|
+
|
|
318
311
|
// Check RAM (volatile memory)
|
|
319
312
|
results.ram = await checkRAM(minMemoryMB, options);
|
|
320
313
|
if (!results.ram.success) {
|
|
@@ -323,13 +316,12 @@ export const checkSystem = async (requirements = {}, options = {}) => {
|
|
|
323
316
|
process.exit(1);
|
|
324
317
|
}
|
|
325
318
|
}
|
|
326
|
-
|
|
319
|
+
|
|
327
320
|
return results;
|
|
328
321
|
};
|
|
329
322
|
|
|
330
323
|
// CLI interface when run directly
|
|
331
324
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
332
|
-
|
|
333
325
|
// Create yargs instance with all options
|
|
334
326
|
const yargsInstance = yargs(hideBin(process.argv))
|
|
335
327
|
.scriptName('memory-check.mjs')
|
|
@@ -338,66 +330,66 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
338
330
|
alias: 'm',
|
|
339
331
|
type: 'number',
|
|
340
332
|
description: 'Minimum required memory in MB',
|
|
341
|
-
default: 256
|
|
333
|
+
default: 256,
|
|
342
334
|
})
|
|
343
335
|
.option('min-disk-space', {
|
|
344
336
|
alias: 'd',
|
|
345
337
|
type: 'number',
|
|
346
338
|
description: 'Minimum required disk space in MB',
|
|
347
|
-
default: 500
|
|
339
|
+
default: 500,
|
|
348
340
|
})
|
|
349
341
|
.option('exit-on-failure', {
|
|
350
342
|
alias: 'e',
|
|
351
343
|
type: 'boolean',
|
|
352
344
|
description: 'Exit with code 1 if any check fails',
|
|
353
|
-
default: false
|
|
345
|
+
default: false,
|
|
354
346
|
})
|
|
355
347
|
.option('json', {
|
|
356
348
|
alias: 'j',
|
|
357
349
|
type: 'boolean',
|
|
358
350
|
description: 'Output results as JSON',
|
|
359
|
-
default: false
|
|
351
|
+
default: false,
|
|
360
352
|
})
|
|
361
353
|
.option('quiet', {
|
|
362
354
|
alias: 'q',
|
|
363
355
|
type: 'boolean',
|
|
364
356
|
description: 'Suppress detailed output (only show final status)',
|
|
365
|
-
default: false
|
|
357
|
+
default: false,
|
|
366
358
|
})
|
|
367
359
|
.option('log-file', {
|
|
368
360
|
alias: 'l',
|
|
369
361
|
type: 'string',
|
|
370
|
-
description: 'Path to log file for output'
|
|
362
|
+
description: 'Path to log file for output',
|
|
371
363
|
})
|
|
372
364
|
.help('h')
|
|
373
365
|
.alias('h', 'help');
|
|
374
|
-
|
|
366
|
+
|
|
375
367
|
// Check for help before parsing
|
|
376
368
|
if (process.argv.includes('--help') || process.argv.includes('-h')) {
|
|
377
369
|
yargsInstance.showHelp();
|
|
378
370
|
process.exit(0);
|
|
379
371
|
}
|
|
380
|
-
|
|
372
|
+
|
|
381
373
|
const argv = await yargsInstance.parseAsync();
|
|
382
|
-
|
|
374
|
+
|
|
383
375
|
// If we get here, help wasn't requested or yargs didn't handle it
|
|
384
376
|
// Set up logging based on options
|
|
385
377
|
if (argv.logFile) {
|
|
386
378
|
setLogFile(argv.logFile);
|
|
387
379
|
}
|
|
388
|
-
|
|
380
|
+
|
|
389
381
|
// Create appropriate log function based on quiet mode
|
|
390
382
|
const log = argv.quiet ? async () => {} : libLog;
|
|
391
|
-
|
|
383
|
+
|
|
392
384
|
const results = await checkSystem(
|
|
393
385
|
{
|
|
394
386
|
minMemoryMB: argv.minMemory,
|
|
395
387
|
minDiskSpaceMB: argv.minDiskSpace,
|
|
396
|
-
exitOnFailure: argv.exitOnFailure
|
|
388
|
+
exitOnFailure: argv.exitOnFailure,
|
|
397
389
|
},
|
|
398
390
|
{ log }
|
|
399
391
|
);
|
|
400
|
-
|
|
392
|
+
|
|
401
393
|
if (argv.json) {
|
|
402
394
|
console.log(JSON.stringify(results, null, 2));
|
|
403
395
|
} else if (!argv.quiet) {
|
|
@@ -407,7 +399,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
407
399
|
console.log(`Disk: ${results.disk.success ? '✅' : '❌'} ${results.disk.availableMB}MB available (${results.disk.required}MB required)`);
|
|
408
400
|
console.log(`Overall: ${results.success ? '✅ All checks passed' : '❌ Some checks failed'}`);
|
|
409
401
|
}
|
|
410
|
-
|
|
402
|
+
|
|
411
403
|
if (!results.success && argv.exitOnFailure) {
|
|
412
404
|
process.exit(1);
|
|
413
405
|
}
|
|
@@ -416,4 +408,4 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
|
|
416
408
|
// Restore CI if it was set (at the very end, after yargs has processed everything)
|
|
417
409
|
if (originalCI !== undefined) {
|
|
418
410
|
process.env.CI = originalCI;
|
|
419
|
-
}
|
|
411
|
+
}
|
|
@@ -7,50 +7,50 @@
|
|
|
7
7
|
|
|
8
8
|
// Claude models (Anthropic API)
|
|
9
9
|
export const claudeModels = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
sonnet: 'claude-sonnet-4-5-20250929', // Sonnet 4.5
|
|
11
|
+
opus: 'claude-opus-4-5-20251101', // Opus 4.5
|
|
12
|
+
haiku: 'claude-haiku-4-5-20251001', // Haiku 4.5
|
|
13
13
|
'haiku-3-5': 'claude-3-5-haiku-20241022', // Haiku 3.5
|
|
14
|
-
'haiku-3': 'claude-3-haiku-20240307',
|
|
14
|
+
'haiku-3': 'claude-3-haiku-20240307', // Haiku 3
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
// Agent models (OpenCode API via agent CLI)
|
|
18
18
|
export const agentModels = {
|
|
19
|
-
|
|
19
|
+
grok: 'opencode/grok-code',
|
|
20
20
|
'grok-code': 'opencode/grok-code',
|
|
21
21
|
'grok-code-fast-1': 'opencode/grok-code',
|
|
22
22
|
'big-pickle': 'opencode/big-pickle',
|
|
23
23
|
'gpt-5-nano': 'openai/gpt-5-nano',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
sonnet: 'anthropic/claude-3-5-sonnet',
|
|
25
|
+
haiku: 'anthropic/claude-3-5-haiku',
|
|
26
|
+
opus: 'anthropic/claude-3-opus',
|
|
27
27
|
'gemini-3-pro': 'google/gemini-3-pro',
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// OpenCode models (OpenCode API)
|
|
31
31
|
export const opencodeModels = {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
gpt4: 'openai/gpt-4',
|
|
33
|
+
gpt4o: 'openai/gpt-4o',
|
|
34
|
+
claude: 'anthropic/claude-3-5-sonnet',
|
|
35
|
+
sonnet: 'anthropic/claude-3-5-sonnet',
|
|
36
|
+
opus: 'anthropic/claude-3-opus',
|
|
37
|
+
gemini: 'google/gemini-pro',
|
|
38
|
+
grok: 'opencode/grok-code',
|
|
39
39
|
'grok-code': 'opencode/grok-code',
|
|
40
40
|
'grok-code-fast-1': 'opencode/grok-code',
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
// Codex models (OpenAI API)
|
|
44
44
|
export const codexModels = {
|
|
45
|
-
|
|
45
|
+
gpt5: 'gpt-5',
|
|
46
46
|
'gpt5-codex': 'gpt-5-codex',
|
|
47
|
-
|
|
47
|
+
o3: 'o3',
|
|
48
48
|
'o3-mini': 'o3-mini',
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
gpt4: 'gpt-4',
|
|
50
|
+
gpt4o: 'gpt-4o',
|
|
51
|
+
claude: 'claude-3-5-sonnet',
|
|
52
|
+
sonnet: 'claude-3-5-sonnet',
|
|
53
|
+
opus: 'claude-3-opus',
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
/**
|
|
@@ -96,10 +96,7 @@ export const isModelCompatibleWithTool = (tool, model) => {
|
|
|
96
96
|
return mappedModel.includes('/') || Object.keys(opencodeModels).includes(model);
|
|
97
97
|
case 'codex':
|
|
98
98
|
// Codex accepts OpenAI and some Claude models
|
|
99
|
-
return Object.keys(codexModels).includes(model) ||
|
|
100
|
-
mappedModel.startsWith('gpt-') ||
|
|
101
|
-
mappedModel.startsWith('o3') ||
|
|
102
|
-
mappedModel.startsWith('claude-');
|
|
99
|
+
return Object.keys(codexModels).includes(model) || mappedModel.startsWith('gpt-') || mappedModel.startsWith('o3') || mappedModel.startsWith('claude-');
|
|
103
100
|
default:
|
|
104
101
|
return true;
|
|
105
102
|
}
|
|
@@ -110,7 +107,7 @@ export const isModelCompatibleWithTool = (tool, model) => {
|
|
|
110
107
|
* @param {string} tool - The tool name
|
|
111
108
|
* @returns {string[]} Array of valid model names
|
|
112
109
|
*/
|
|
113
|
-
export const getValidModelsForTool =
|
|
110
|
+
export const getValidModelsForTool = tool => {
|
|
114
111
|
switch (tool) {
|
|
115
112
|
case 'claude':
|
|
116
113
|
return Object.keys(claudeModels);
|
|
@@ -136,10 +133,6 @@ export const validateToolModelCompatibility = (tool, model) => {
|
|
|
136
133
|
const validModels = getValidModelsForTool(tool);
|
|
137
134
|
const mappedModel = mapModelForTool(tool, model);
|
|
138
135
|
|
|
139
|
-
throw new Error(
|
|
140
|
-
`Model '${model}' (mapped to '${mappedModel}') is not compatible with --tool ${tool}.\n` +
|
|
141
|
-
`Valid models for ${tool}: ${validModels.join(', ')}\n` +
|
|
142
|
-
'Hint: Different tools use different model APIs and naming conventions.'
|
|
143
|
-
);
|
|
136
|
+
throw new Error(`Model '${model}' (mapped to '${mappedModel}') is not compatible with --tool ${tool}.\n` + `Valid models for ${tool}: ${validModels.join(', ')}\n` + 'Hint: Different tools use different model APIs and naming conventions.');
|
|
144
137
|
}
|
|
145
138
|
};
|
|
@@ -14,9 +14,9 @@ import { log } from './lib.mjs';
|
|
|
14
14
|
// These are the "known good" model names that we accept
|
|
15
15
|
export const CLAUDE_MODELS = {
|
|
16
16
|
// Short aliases
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
sonnet: 'claude-sonnet-4-5-20250929',
|
|
18
|
+
opus: 'claude-opus-4-5-20251101',
|
|
19
|
+
haiku: 'claude-haiku-4-5-20251001',
|
|
20
20
|
'haiku-3-5': 'claude-3-5-haiku-20241022',
|
|
21
21
|
'haiku-3': 'claude-3-haiku-20240307',
|
|
22
22
|
// Full model IDs (also valid inputs)
|
|
@@ -28,13 +28,13 @@ export const CLAUDE_MODELS = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
export const OPENCODE_MODELS = {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
31
|
+
gpt4: 'openai/gpt-4',
|
|
32
|
+
gpt4o: 'openai/gpt-4o',
|
|
33
|
+
claude: 'anthropic/claude-3-5-sonnet',
|
|
34
|
+
sonnet: 'anthropic/claude-3-5-sonnet',
|
|
35
|
+
opus: 'anthropic/claude-3-opus',
|
|
36
|
+
gemini: 'google/gemini-pro',
|
|
37
|
+
grok: 'opencode/grok-code',
|
|
38
38
|
'grok-code': 'opencode/grok-code',
|
|
39
39
|
'grok-code-fast-1': 'opencode/grok-code',
|
|
40
40
|
// Full model IDs
|
|
@@ -47,17 +47,17 @@ export const OPENCODE_MODELS = {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
export const CODEX_MODELS = {
|
|
50
|
-
|
|
50
|
+
gpt5: 'gpt-5',
|
|
51
51
|
'gpt-5': 'gpt-5',
|
|
52
52
|
'gpt5-codex': 'gpt-5-codex',
|
|
53
53
|
'gpt-5-codex': 'gpt-5-codex',
|
|
54
|
-
|
|
54
|
+
o3: 'o3',
|
|
55
55
|
'o3-mini': 'o3-mini',
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
gpt4: 'gpt-4',
|
|
57
|
+
gpt4o: 'gpt-4o',
|
|
58
|
+
claude: 'claude-3-5-sonnet',
|
|
59
|
+
sonnet: 'claude-3-5-sonnet',
|
|
60
|
+
opus: 'claude-3-opus',
|
|
61
61
|
// Full model IDs
|
|
62
62
|
'gpt-4': 'gpt-4',
|
|
63
63
|
'gpt-4o': 'gpt-4o',
|
|
@@ -67,15 +67,15 @@ export const CODEX_MODELS = {
|
|
|
67
67
|
|
|
68
68
|
export const AGENT_MODELS = {
|
|
69
69
|
// Free models (via OpenCode)
|
|
70
|
-
|
|
70
|
+
grok: 'opencode/grok-code',
|
|
71
71
|
'grok-code': 'opencode/grok-code',
|
|
72
72
|
'grok-code-fast-1': 'opencode/grok-code',
|
|
73
73
|
'big-pickle': 'opencode/big-pickle',
|
|
74
74
|
'gpt-5-nano': 'openai/gpt-5-nano',
|
|
75
75
|
// Premium models (requires OpenCode Zen subscription)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
sonnet: 'anthropic/claude-3-5-sonnet',
|
|
77
|
+
haiku: 'anthropic/claude-3-5-haiku',
|
|
78
|
+
opus: 'anthropic/claude-3-opus',
|
|
79
79
|
'gemini-3-pro': 'google/gemini-3-pro',
|
|
80
80
|
// Full model IDs
|
|
81
81
|
'opencode/grok-code': 'opencode/grok-code',
|
|
@@ -92,7 +92,7 @@ export const AGENT_MODELS = {
|
|
|
92
92
|
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent')
|
|
93
93
|
* @returns {Object} The model mapping for the tool
|
|
94
94
|
*/
|
|
95
|
-
export const getModelMapForTool =
|
|
95
|
+
export const getModelMapForTool = tool => {
|
|
96
96
|
switch (tool) {
|
|
97
97
|
case 'opencode':
|
|
98
98
|
return OPENCODE_MODELS;
|
|
@@ -111,7 +111,7 @@ export const getModelMapForTool = (tool) => {
|
|
|
111
111
|
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent')
|
|
112
112
|
* @returns {string[]} Array of available model short names
|
|
113
113
|
*/
|
|
114
|
-
export const getAvailableModelNames =
|
|
114
|
+
export const getAvailableModelNames = tool => {
|
|
115
115
|
const modelMap = getModelMapForTool(tool);
|
|
116
116
|
// Get unique short names (aliases) - exclude full model IDs that contain '/' or long claude- prefixed IDs
|
|
117
117
|
const aliases = Object.keys(modelMap).filter(key => {
|
|
@@ -121,8 +121,8 @@ export const getAvailableModelNames = (tool) => {
|
|
|
121
121
|
// - Full gpt- prefixed IDs with version numbers (e.g., 'gpt-4', 'gpt-4o')
|
|
122
122
|
// But keep short names like 'o3', 'o3-mini', 'gpt5', etc.
|
|
123
123
|
if (key.includes('/')) return false;
|
|
124
|
-
if (key.match(/^claude-.*-\d{8}$/)) return false;
|
|
125
|
-
if (key.match(/^gpt-\d+/)) return false;
|
|
124
|
+
if (key.match(/^claude-.*-\d{8}$/)) return false; // Full claude model IDs with date
|
|
125
|
+
if (key.match(/^gpt-\d+/)) return false; // Full gpt-N model IDs
|
|
126
126
|
return true;
|
|
127
127
|
});
|
|
128
128
|
return [...new Set(aliases)];
|
|
@@ -162,8 +162,8 @@ export const levenshteinDistance = (a, b) => {
|
|
|
162
162
|
} else {
|
|
163
163
|
matrix[i][j] = Math.min(
|
|
164
164
|
matrix[i - 1][j - 1] + 1, // substitution
|
|
165
|
-
matrix[i][j - 1] + 1,
|
|
166
|
-
matrix[i - 1][j] + 1
|
|
165
|
+
matrix[i][j - 1] + 1, // insertion
|
|
166
|
+
matrix[i - 1][j] + 1 // deletion
|
|
167
167
|
);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -184,7 +184,7 @@ export const findSimilarModels = (input, validModels, maxSuggestions = 3, maxDis
|
|
|
184
184
|
const suggestions = validModels
|
|
185
185
|
.map(model => ({
|
|
186
186
|
model,
|
|
187
|
-
distance: levenshteinDistance(input, model)
|
|
187
|
+
distance: levenshteinDistance(input, model),
|
|
188
188
|
}))
|
|
189
189
|
.filter(({ distance }) => distance <= maxDistance)
|
|
190
190
|
.sort((a, b) => a.distance - b.distance)
|
|
@@ -205,7 +205,7 @@ export const validateModelName = (model, tool = 'claude') => {
|
|
|
205
205
|
return {
|
|
206
206
|
valid: false,
|
|
207
207
|
message: 'Model name is required',
|
|
208
|
-
suggestions: []
|
|
208
|
+
suggestions: [],
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
211
|
|
|
@@ -219,7 +219,7 @@ export const validateModelName = (model, tool = 'claude') => {
|
|
|
219
219
|
if (matchedKey) {
|
|
220
220
|
return {
|
|
221
221
|
valid: true,
|
|
222
|
-
mappedModel: modelMap[matchedKey]
|
|
222
|
+
mappedModel: modelMap[matchedKey],
|
|
223
223
|
};
|
|
224
224
|
}
|
|
225
225
|
|
|
@@ -238,7 +238,7 @@ export const validateModelName = (model, tool = 'claude') => {
|
|
|
238
238
|
return {
|
|
239
239
|
valid: false,
|
|
240
240
|
message,
|
|
241
|
-
suggestions
|
|
241
|
+
suggestions,
|
|
242
242
|
};
|
|
243
243
|
};
|
|
244
244
|
|