@link-assistant/hive-mind 0.46.1 โ 0.47.1
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 +20 -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/lib.mjs
CHANGED
|
@@ -15,7 +15,7 @@ try {
|
|
|
15
15
|
if (global.verboseMode) {
|
|
16
16
|
console.debug('Sentry module not available:', _error?.message || 'Import failed');
|
|
17
17
|
}
|
|
18
|
-
reportError =
|
|
18
|
+
reportError = err => {
|
|
19
19
|
// Silent no-op when Sentry is not available
|
|
20
20
|
if (global.verboseMode) {
|
|
21
21
|
console.debug('Sentry not available for error reporting:', err?.message);
|
|
@@ -44,7 +44,7 @@ export let logFile = null;
|
|
|
44
44
|
* Set the log file path
|
|
45
45
|
* @param {string} path - Path to the log file
|
|
46
46
|
*/
|
|
47
|
-
export const setLogFile =
|
|
47
|
+
export const setLogFile = path => {
|
|
48
48
|
logFile = path;
|
|
49
49
|
};
|
|
50
50
|
|
|
@@ -62,7 +62,7 @@ export const getLogFile = () => {
|
|
|
62
62
|
*/
|
|
63
63
|
export const getAbsoluteLogPath = async () => {
|
|
64
64
|
if (!logFile) return null;
|
|
65
|
-
const path =
|
|
65
|
+
const path = await use('path');
|
|
66
66
|
return path.resolve(logFile);
|
|
67
67
|
};
|
|
68
68
|
|
|
@@ -85,19 +85,19 @@ export const log = async (message, options = {}) => {
|
|
|
85
85
|
// Write to file if log file is set
|
|
86
86
|
if (logFile) {
|
|
87
87
|
const logMessage = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message}`;
|
|
88
|
-
await fs.appendFile(logFile, logMessage + '\n').catch(
|
|
88
|
+
await fs.appendFile(logFile, logMessage + '\n').catch(error => {
|
|
89
89
|
// Silent fail for file append errors to avoid infinite loop
|
|
90
90
|
// but report to Sentry in verbose mode
|
|
91
91
|
if (global.verboseMode) {
|
|
92
92
|
reportError(error, {
|
|
93
93
|
context: 'log_file_append',
|
|
94
94
|
level: 'debug',
|
|
95
|
-
logFile
|
|
95
|
+
logFile,
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
// Write to console based on level
|
|
102
102
|
switch (level) {
|
|
103
103
|
case 'error':
|
|
@@ -125,19 +125,18 @@ export const log = async (message, options = {}) => {
|
|
|
125
125
|
*/
|
|
126
126
|
export const maskToken = (token, options = {}) => {
|
|
127
127
|
const { minLength = 12, startChars = 5, endChars = 5 } = options;
|
|
128
|
-
|
|
128
|
+
|
|
129
129
|
if (!token || token.length < minLength) {
|
|
130
130
|
return token; // Don't mask very short strings
|
|
131
131
|
}
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
const start = token.substring(0, startChars);
|
|
134
134
|
const end = token.substring(token.length - endChars);
|
|
135
135
|
const middle = '*'.repeat(Math.max(token.length - (startChars + endChars), 3));
|
|
136
|
-
|
|
136
|
+
|
|
137
137
|
return start + middle + end;
|
|
138
138
|
};
|
|
139
139
|
|
|
140
|
-
|
|
141
140
|
/**
|
|
142
141
|
* Format timestamps for use in filenames
|
|
143
142
|
* @param {Date} [date=new Date()] - Date to format
|
|
@@ -152,7 +151,7 @@ export const formatTimestamp = (date = new Date()) => {
|
|
|
152
151
|
* @param {string} name - Name to sanitize
|
|
153
152
|
* @returns {string} Sanitized filename
|
|
154
153
|
*/
|
|
155
|
-
export const sanitizeFileName =
|
|
154
|
+
export const sanitizeFileName = name => {
|
|
156
155
|
return name.replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase();
|
|
157
156
|
};
|
|
158
157
|
|
|
@@ -176,7 +175,7 @@ export const getPlatformInfo = () => {
|
|
|
176
175
|
arch: process.arch,
|
|
177
176
|
runtime: getRuntime(),
|
|
178
177
|
nodeVersion: process.versions?.node,
|
|
179
|
-
bunVersion: process.versions?.bun
|
|
178
|
+
bunVersion: process.versions?.bun,
|
|
180
179
|
};
|
|
181
180
|
};
|
|
182
181
|
|
|
@@ -196,7 +195,7 @@ export const safeJsonParse = (text, defaultValue = null) => {
|
|
|
196
195
|
reportError(error, {
|
|
197
196
|
context: 'safe_json_parse',
|
|
198
197
|
level: 'debug',
|
|
199
|
-
textPreview: text?.substring(0, 100)
|
|
198
|
+
textPreview: text?.substring(0, 100),
|
|
200
199
|
});
|
|
201
200
|
}
|
|
202
201
|
return defaultValue;
|
|
@@ -208,7 +207,7 @@ export const safeJsonParse = (text, defaultValue = null) => {
|
|
|
208
207
|
* @param {number} ms - Milliseconds to sleep
|
|
209
208
|
* @returns {Promise<void>}
|
|
210
209
|
*/
|
|
211
|
-
export const sleep =
|
|
210
|
+
export const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
212
211
|
|
|
213
212
|
/**
|
|
214
213
|
* Retry operations with exponential backoff
|
|
@@ -232,7 +231,7 @@ export const retry = async (fn, options = {}) => {
|
|
|
232
231
|
context: 'retry_operation',
|
|
233
232
|
attempt,
|
|
234
233
|
maxAttempts,
|
|
235
|
-
willRetry: attempt < maxAttempts
|
|
234
|
+
willRetry: attempt < maxAttempts,
|
|
236
235
|
});
|
|
237
236
|
|
|
238
237
|
if (attempt === maxAttempts) throw error;
|
|
@@ -252,13 +251,13 @@ export const retry = async (fn, options = {}) => {
|
|
|
252
251
|
*/
|
|
253
252
|
export const formatBytes = (bytes, decimals = 2) => {
|
|
254
253
|
if (bytes === 0) return '0 Bytes';
|
|
255
|
-
|
|
254
|
+
|
|
256
255
|
const k = 1024;
|
|
257
256
|
const dm = decimals < 0 ? 0 : decimals;
|
|
258
257
|
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
259
|
-
|
|
258
|
+
|
|
260
259
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
261
|
-
|
|
260
|
+
|
|
262
261
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
263
262
|
};
|
|
264
263
|
|
|
@@ -282,7 +281,7 @@ export const measureTime = async (fn, label = 'Operation') => {
|
|
|
282
281
|
reportError(error, {
|
|
283
282
|
context: 'measure_time',
|
|
284
283
|
operation: label,
|
|
285
|
-
duration
|
|
284
|
+
duration,
|
|
286
285
|
});
|
|
287
286
|
throw error;
|
|
288
287
|
}
|
|
@@ -293,15 +292,15 @@ export const measureTime = async (fn, label = 'Operation') => {
|
|
|
293
292
|
* @param {Error|string} error - Error object or message
|
|
294
293
|
* @returns {string} Cleaned error message
|
|
295
294
|
*/
|
|
296
|
-
export const cleanErrorMessage =
|
|
295
|
+
export const cleanErrorMessage = error => {
|
|
297
296
|
let message = error.message || error.toString();
|
|
298
|
-
|
|
297
|
+
|
|
299
298
|
// Remove common noise from error messages
|
|
300
299
|
message = message.split('\n')[0]; // Take only first line
|
|
301
300
|
message = message.replace(/^Command failed: /, ''); // Remove "Command failed: " prefix
|
|
302
301
|
message = message.replace(/^Error: /, ''); // Remove redundant "Error: " prefix
|
|
303
302
|
message = message.replace(/^\/bin\/sh: \d+: /, ''); // Remove shell path info
|
|
304
|
-
|
|
303
|
+
|
|
305
304
|
return message;
|
|
306
305
|
};
|
|
307
306
|
|
|
@@ -333,17 +332,8 @@ export const formatAligned = (icon, label, value, indent = 0) => {
|
|
|
333
332
|
* @param {string} [options.level='error'] - Log level
|
|
334
333
|
* @returns {Promise<void>}
|
|
335
334
|
*/
|
|
336
|
-
export const displayFormattedError = async
|
|
337
|
-
const {
|
|
338
|
-
title,
|
|
339
|
-
what,
|
|
340
|
-
details,
|
|
341
|
-
causes,
|
|
342
|
-
fixes,
|
|
343
|
-
workDir,
|
|
344
|
-
log: logFn = log,
|
|
345
|
-
level = 'error'
|
|
346
|
-
} = options;
|
|
335
|
+
export const displayFormattedError = async options => {
|
|
336
|
+
const { title, what, details, causes, fixes, workDir, log: logFn = log, level = 'error' } = options;
|
|
347
337
|
|
|
348
338
|
await logFn('');
|
|
349
339
|
await logFn(`โ ${title}`, { level });
|
|
@@ -387,7 +377,7 @@ export const displayFormattedError = async (options) => {
|
|
|
387
377
|
|
|
388
378
|
// Always show the log file path if it exists - using absolute path
|
|
389
379
|
if (logFile) {
|
|
390
|
-
const path =
|
|
380
|
+
const path = await use('path');
|
|
391
381
|
const absoluteLogPath = path.resolve(logFile);
|
|
392
382
|
await logFn(` ๐ Full log file: ${absoluteLogPath}`);
|
|
393
383
|
await logFn('');
|
|
@@ -400,33 +390,34 @@ export const displayFormattedError = async (options) => {
|
|
|
400
390
|
* @param {boolean} [argv.autoCleanup] - Whether auto-cleanup is enabled
|
|
401
391
|
* @returns {Promise<void>}
|
|
402
392
|
*/
|
|
403
|
-
export const cleanupTempDirectories = async
|
|
393
|
+
export const cleanupTempDirectories = async argv => {
|
|
404
394
|
if (!argv || !argv.autoCleanup) {
|
|
405
395
|
return;
|
|
406
396
|
}
|
|
407
|
-
|
|
397
|
+
|
|
408
398
|
// Dynamic import for command-stream
|
|
409
399
|
const { $ } = await use('command-stream');
|
|
410
|
-
|
|
400
|
+
|
|
411
401
|
try {
|
|
412
402
|
await log('\n๐งน Auto-cleanup enabled, removing temporary directories...');
|
|
413
403
|
await log(' โ ๏ธ Executing: sudo rm -rf /tmp/* /var/tmp/*', { verbose: true });
|
|
414
|
-
|
|
404
|
+
|
|
415
405
|
// Execute cleanup command using command-stream
|
|
416
406
|
const cleanupCommand = $`sudo rm -rf /tmp/* /var/tmp/*`;
|
|
417
|
-
|
|
407
|
+
|
|
418
408
|
let exitCode = 0;
|
|
419
409
|
for await (const chunk of cleanupCommand.stream()) {
|
|
420
410
|
if (chunk.type === 'stderr') {
|
|
421
411
|
const error = chunk.data.toString().trim();
|
|
422
|
-
if (error && !error.includes('cannot remove')) {
|
|
412
|
+
if (error && !error.includes('cannot remove')) {
|
|
413
|
+
// Ignore "cannot remove" warnings for files in use
|
|
423
414
|
await log(` [cleanup WARNING] ${error}`, { level: 'warn', verbose: true });
|
|
424
415
|
}
|
|
425
416
|
} else if (chunk.type === 'exit') {
|
|
426
417
|
exitCode = chunk.code;
|
|
427
418
|
}
|
|
428
419
|
}
|
|
429
|
-
|
|
420
|
+
|
|
430
421
|
if (exitCode === 0) {
|
|
431
422
|
await log(' โ
Temporary directories cleaned successfully');
|
|
432
423
|
} else {
|
|
@@ -435,7 +426,7 @@ export const cleanupTempDirectories = async (argv) => {
|
|
|
435
426
|
} catch (error) {
|
|
436
427
|
reportError(error, {
|
|
437
428
|
context: 'cleanup_temp_directories',
|
|
438
|
-
autoCleanup: argv?.autoCleanup
|
|
429
|
+
autoCleanup: argv?.autoCleanup,
|
|
439
430
|
});
|
|
440
431
|
await log(` โ Error during cleanup: ${cleanErrorMessage(error)}`, { level: 'error' });
|
|
441
432
|
// Don't fail the entire process if cleanup fails
|
|
@@ -461,7 +452,7 @@ export default {
|
|
|
461
452
|
cleanErrorMessage,
|
|
462
453
|
formatAligned,
|
|
463
454
|
displayFormattedError,
|
|
464
|
-
cleanupTempDirectories
|
|
455
|
+
cleanupTempDirectories,
|
|
465
456
|
};
|
|
466
457
|
|
|
467
458
|
/**
|
|
@@ -469,7 +460,7 @@ export default {
|
|
|
469
460
|
* @returns {Promise<string>} Version string
|
|
470
461
|
*/
|
|
471
462
|
export const getVersionInfo = async () => {
|
|
472
|
-
const path =
|
|
463
|
+
const path = await use('path');
|
|
473
464
|
const $ = (await use('zx')).$;
|
|
474
465
|
const { getGitVersionAsync } = await import('./git.lib.mjs');
|
|
475
466
|
|
|
@@ -487,4 +478,4 @@ export const getVersionInfo = async () => {
|
|
|
487
478
|
};
|
|
488
479
|
|
|
489
480
|
// Export reportError for other modules that may import it
|
|
490
|
-
export { reportError, reportWarning };
|
|
481
|
+
export { reportError, reportWarning };
|
package/src/lino.lib.mjs
CHANGED
|
@@ -136,7 +136,7 @@ export class LinksNotationManager {
|
|
|
136
136
|
parsed: this.parse(content),
|
|
137
137
|
numericIds: this.parseNumericIds(content),
|
|
138
138
|
stringValues: this.parseStringValues(content),
|
|
139
|
-
file: cacheFile
|
|
139
|
+
file: cacheFile,
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
@@ -170,7 +170,7 @@ export class LinksNotationManager {
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
export const CACHE_FILES = {
|
|
173
|
-
TELEGRAM_CHATS: 'telegram-chats.lino'
|
|
173
|
+
TELEGRAM_CHATS: 'telegram-chats.lino',
|
|
174
174
|
};
|
|
175
175
|
|
|
176
176
|
export const lino = new LinksNotationManager();
|
|
@@ -26,22 +26,22 @@ export async function detectCITools(workDir) {
|
|
|
26
26
|
pytest: false,
|
|
27
27
|
nox: false,
|
|
28
28
|
black: false,
|
|
29
|
-
flake8: false
|
|
29
|
+
flake8: false,
|
|
30
30
|
},
|
|
31
31
|
javascript: {
|
|
32
32
|
eslint: false,
|
|
33
33
|
prettier: false,
|
|
34
34
|
jest: false,
|
|
35
|
-
vitest: false
|
|
35
|
+
vitest: false,
|
|
36
36
|
},
|
|
37
37
|
rust: {
|
|
38
38
|
rustfmt: false,
|
|
39
39
|
clippy: false,
|
|
40
|
-
cargoTest: false
|
|
40
|
+
cargoTest: false,
|
|
41
41
|
},
|
|
42
42
|
general: {
|
|
43
|
-
preCommit: false
|
|
44
|
-
}
|
|
43
|
+
preCommit: false,
|
|
44
|
+
},
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
try {
|
|
@@ -134,7 +134,6 @@ export async function detectCITools(workDir) {
|
|
|
134
134
|
} catch {
|
|
135
135
|
// File doesn't exist, pre-commit not configured
|
|
136
136
|
}
|
|
137
|
-
|
|
138
137
|
} catch (err) {
|
|
139
138
|
console.error('Error detecting CI tools:', err.message);
|
|
140
139
|
}
|
|
@@ -153,7 +152,7 @@ export async function runLocalCIChecks(workDir, tools, options = {}) {
|
|
|
153
152
|
const results = {
|
|
154
153
|
success: true,
|
|
155
154
|
checks: [],
|
|
156
|
-
errors: []
|
|
155
|
+
errors: [],
|
|
157
156
|
};
|
|
158
157
|
|
|
159
158
|
const verbose = options.verbose || false;
|
|
@@ -253,7 +252,7 @@ async function runCheck(workDir, command, name, verbose) {
|
|
|
253
252
|
command,
|
|
254
253
|
success: false,
|
|
255
254
|
output: '',
|
|
256
|
-
error: ''
|
|
255
|
+
error: '',
|
|
257
256
|
};
|
|
258
257
|
|
|
259
258
|
try {
|
|
@@ -306,12 +305,14 @@ export function generateCICheckReport(results) {
|
|
|
306
305
|
|
|
307
306
|
if (failed > 0) {
|
|
308
307
|
lines.push('Failed checks:');
|
|
309
|
-
results.checks
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
lines.push(`
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
results.checks
|
|
309
|
+
.filter(c => !c.success)
|
|
310
|
+
.forEach(check => {
|
|
311
|
+
lines.push(` โ ${check.name}`);
|
|
312
|
+
if (check.output) {
|
|
313
|
+
lines.push(` ${check.output.split('\n')[0]}`);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
if (results.success) {
|