@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.
Files changed (63) hide show
  1. package/CHANGELOG.md +10 -15
  2. package/README.md +42 -8
  3. package/package.json +16 -3
  4. package/src/agent.lib.mjs +49 -70
  5. package/src/agent.prompts.lib.mjs +6 -20
  6. package/src/buildUserMention.lib.mjs +4 -17
  7. package/src/claude-limits.lib.mjs +15 -15
  8. package/src/claude.lib.mjs +617 -626
  9. package/src/claude.prompts.lib.mjs +7 -22
  10. package/src/codex.lib.mjs +39 -71
  11. package/src/codex.prompts.lib.mjs +6 -20
  12. package/src/config.lib.mjs +3 -16
  13. package/src/contributing-guidelines.lib.mjs +5 -18
  14. package/src/exit-handler.lib.mjs +4 -4
  15. package/src/git.lib.mjs +7 -7
  16. package/src/github-issue-creator.lib.mjs +17 -17
  17. package/src/github-linking.lib.mjs +8 -33
  18. package/src/github.batch.lib.mjs +20 -16
  19. package/src/github.graphql.lib.mjs +18 -18
  20. package/src/github.lib.mjs +89 -91
  21. package/src/hive.config.lib.mjs +50 -50
  22. package/src/hive.mjs +1293 -1296
  23. package/src/instrument.mjs +7 -11
  24. package/src/interactive-mode.lib.mjs +112 -138
  25. package/src/lenv-reader.lib.mjs +1 -6
  26. package/src/lib.mjs +36 -45
  27. package/src/lino.lib.mjs +2 -2
  28. package/src/local-ci-checks.lib.mjs +15 -14
  29. package/src/memory-check.mjs +52 -60
  30. package/src/model-mapping.lib.mjs +25 -32
  31. package/src/model-validation.lib.mjs +31 -31
  32. package/src/opencode.lib.mjs +37 -62
  33. package/src/opencode.prompts.lib.mjs +7 -21
  34. package/src/protect-branch.mjs +14 -15
  35. package/src/review.mjs +28 -27
  36. package/src/reviewers-hive.mjs +64 -69
  37. package/src/sentry.lib.mjs +13 -10
  38. package/src/solve.auto-continue.lib.mjs +48 -38
  39. package/src/solve.auto-pr.lib.mjs +111 -69
  40. package/src/solve.branch-errors.lib.mjs +17 -46
  41. package/src/solve.branch.lib.mjs +16 -23
  42. package/src/solve.config.lib.mjs +263 -261
  43. package/src/solve.error-handlers.lib.mjs +21 -79
  44. package/src/solve.execution.lib.mjs +10 -18
  45. package/src/solve.feedback.lib.mjs +25 -46
  46. package/src/solve.mjs +59 -60
  47. package/src/solve.preparation.lib.mjs +10 -36
  48. package/src/solve.repo-setup.lib.mjs +4 -19
  49. package/src/solve.repository.lib.mjs +37 -37
  50. package/src/solve.results.lib.mjs +32 -46
  51. package/src/solve.session.lib.mjs +7 -22
  52. package/src/solve.validation.lib.mjs +19 -17
  53. package/src/solve.watch.lib.mjs +20 -33
  54. package/src/start-screen.mjs +24 -24
  55. package/src/task.mjs +38 -44
  56. package/src/telegram-bot.mjs +125 -121
  57. package/src/telegram-top-command.lib.mjs +32 -48
  58. package/src/usage-limit.lib.mjs +9 -13
  59. package/src/version-info.lib.mjs +1 -1
  60. package/src/version.lib.mjs +1 -1
  61. package/src/youtrack/solve.youtrack.lib.mjs +3 -8
  62. package/src/youtrack/youtrack-sync.mjs +8 -14
  63. 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 = (err) => {
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 = (path) => {
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 = (await use('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((error) => {
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 = (name) => {
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 = (ms) => new Promise(resolve => setTimeout(resolve, ms));
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 = (error) => {
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 (options) => {
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 = (await use('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 (argv) => {
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')) { // Ignore "cannot remove" warnings for files in use
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 = (await use('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.filter(c => !c.success).forEach(check => {
310
- lines.push(` โŒ ${check.name}`);
311
- if (check.output) {
312
- lines.push(` ${check.output.split('\n')[0]}`);
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) {