@link-assistant/hive-mind 2.11.7 → 2.11.9

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 (42) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/package.json +1 -1
  3. package/src/bidirectional-interactive.lib.mjs +6 -2
  4. package/src/child-exit.lib.mjs +107 -0
  5. package/src/contributing-guidelines.lib.mjs +19 -6
  6. package/src/development-log.lib.mjs +82 -5
  7. package/src/fix.ci-cd-issue.lib.mjs +5 -3
  8. package/src/fix.mjs +5 -2
  9. package/src/github-entity-validation.lib.mjs +4 -1
  10. package/src/github.lib.mjs +3 -3
  11. package/src/hive.mjs +15 -21
  12. package/src/isolation-runner.lib.mjs +5 -2
  13. package/src/lib.mjs +21 -0
  14. package/src/locales/en.lino +10 -0
  15. package/src/locales/hi.lino +10 -0
  16. package/src/locales/ru.lino +10 -0
  17. package/src/locales/zh.lino +10 -0
  18. package/src/log-growth.lib.mjs +94 -0
  19. package/src/option-suggestions.lib.mjs +2 -0
  20. package/src/pull-request-changes.lib.mjs +94 -24
  21. package/src/review.mjs +12 -3
  22. package/src/session-kill-diagnostics.lib.mjs +388 -0
  23. package/src/session-kill-policy.lib.mjs +96 -0
  24. package/src/session-kill-recovery.lib.mjs +256 -0
  25. package/src/session-kill-resume.lib.mjs +175 -0
  26. package/src/session-monitor.kill-sections.lib.mjs +198 -0
  27. package/src/session-monitor.lib.mjs +97 -2
  28. package/src/session-monitor.oom.lib.mjs +148 -0
  29. package/src/session-monitor.stale-executing.lib.mjs +6 -27
  30. package/src/session-resume.lib.mjs +28 -2
  31. package/src/solve.auto-continue.lib.mjs +6 -2
  32. package/src/solve.auto-merge.lib.mjs +1 -1
  33. package/src/solve.config.lib.mjs +14 -0
  34. package/src/solve.keep-working.lib.mjs +7 -2
  35. package/src/solve.minimal-restart-prompt.lib.mjs +11 -3
  36. package/src/solve.preparation.lib.mjs +5 -1
  37. package/src/solve.progress-monitoring.lib.mjs +5 -1
  38. package/src/solve.repository.lib.mjs +5 -2
  39. package/src/solve.results.lib.mjs +13 -8
  40. package/src/task.mjs +5 -3
  41. package/src/telegram-bot.mjs +3 -1
  42. package/src/telegram-command-execution.lib.mjs +5 -2
@@ -407,7 +407,9 @@ export const cleanupClaudeFile = async (tempDir, branchName, claudeCommitHash =
407
407
  // APPROACH 3: Check for modifications before reverting (proactive detection)
408
408
  // This is the main strategy - detect if the file was modified after initial commit
409
409
  await log(` Checking if ${fileName} was modified since initial commit...`, { verbose: true });
410
- const diffResult = await $({ cwd: tempDir })`git diff ${commitToRevert} HEAD -- ${fileName} 2>&1`;
410
+ // Issue #2135: `mirror: false`. Only "is it non-empty" is asked here, and
411
+ // the answer is a file's whole diff.
412
+ const diffResult = await $({ cwd: tempDir, ...QUIET_PROBE })`git diff ${commitToRevert} HEAD -- ${fileName} 2>&1`;
411
413
 
412
414
  if (diffResult.stdout && diffResult.stdout.trim()) {
413
415
  // File was modified after initial commit - use manual approach to avoid conflicts
@@ -746,7 +748,8 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
746
748
  // First, get all PRs from our branch
747
749
  // IMPORTANT: Use --state all to find PRs that may have been merged during the session (Issue #1008)
748
750
  // Without --state all, gh pr list only returns OPEN PRs, missing merged ones
749
- const allBranchPrsResult = await $`gh pr list --repo ${owner}/${repo} --head ${branchName} --state all --json number,url,createdAt,headRefName,title,state,updatedAt,isDraft`;
751
+ // Issue #2135: `mirror: false` - the pull requests found are named below.
752
+ const allBranchPrsResult = await $(QUIET_PROBE)`gh pr list --repo ${owner}/${repo} --head ${branchName} --state all --json number,url,createdAt,headRefName,title,state,updatedAt,isDraft`;
750
753
 
751
754
  if (allBranchPrsResult.code !== 0) {
752
755
  await log(' ⚠️ Failed to check pull requests');
@@ -819,7 +822,7 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
819
822
  // "1 file(s) modified, 1 line(s) added" for a pull request that
820
823
  // changed nothing, because the stats were never checked for being
821
824
  // empty.
822
- const changeStats = await getPullRequestChangeStats({ owner, repo, prNumber: pr.number, $ });
825
+ const changeStats = await getPullRequestChangeStats({ owner, repo, prNumber: pr.number, $, log });
823
826
  if (!changeStats.hasChanges) {
824
827
  await log(` ⚠️ PR #${pr.number} has an empty diff - the description will say so instead of claiming changes`, { level: 'warning' });
825
828
  }
@@ -948,7 +951,9 @@ Fixes ${issueRef}
948
951
 
949
952
  // Get all comments and filter them
950
953
  // Use --paginate to get all comments - GitHub API returns max 30 per page by default
951
- const allCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
954
+ // Issue #2135: `mirror: false` - the counts below are the report; the raw
955
+ // answer is every comment body on the issue.
956
+ const allCommentsResult = await $(QUIET_PROBE)`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
952
957
 
953
958
  if (allCommentsResult.code !== 0) {
954
959
  await log(' ⚠️ Failed to check comments');
@@ -1209,7 +1214,7 @@ export const checkForAiCreatedComments = async (sessionStartTime, owner, repo, p
1209
1214
  // Check comments on the PR first (if we have a PR)
1210
1215
  if (prNumber) {
1211
1216
  // Check PR conversation comments
1212
- const prCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate`;
1217
+ const prCommentsResult = await $(QUIET_PROBE)`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate`;
1213
1218
  if (prCommentsResult.code === 0) {
1214
1219
  const prComments = JSON.parse(prCommentsResult.stdout.toString().trim() || '[]');
1215
1220
  const newPrComments = filterNewAiComments(prComments, 'pr');
@@ -1220,7 +1225,7 @@ export const checkForAiCreatedComments = async (sessionStartTime, owner, repo, p
1220
1225
  }
1221
1226
 
1222
1227
  // Check PR review comments (inline code comments)
1223
- const reviewCommentsResult = await $`gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate`;
1228
+ const reviewCommentsResult = await $(QUIET_PROBE)`gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate`;
1224
1229
  if (reviewCommentsResult.code === 0) {
1225
1230
  const reviewComments = JSON.parse(reviewCommentsResult.stdout.toString().trim() || '[]');
1226
1231
  const newReviewComments = filterNewAiComments(reviewComments, 'review');
@@ -1233,7 +1238,7 @@ export const checkForAiCreatedComments = async (sessionStartTime, owner, repo, p
1233
1238
 
1234
1239
  // Check issue comments (if different from PR number or no PR)
1235
1240
  if (issueNumber && issueNumber !== prNumber) {
1236
- const issueCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
1241
+ const issueCommentsResult = await $(QUIET_PROBE)`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
1237
1242
  if (issueCommentsResult.code === 0) {
1238
1243
  const issueComments = JSON.parse(issueCommentsResult.stdout.toString().trim() || '[]');
1239
1244
  const newIssueComments = filterNewAiComments(issueComments, 'issue');
@@ -1413,7 +1418,7 @@ export const maybeAttachWorkingSessionSummary = async ({ argv, resultSummary, wo
1413
1418
  : null);
1414
1419
  // Issue #2119: a summary posted on a pull request that changed nothing must
1415
1420
  // say so, instead of reading as a report of completed work.
1416
- const changeStats = prNumber ? await getPullRequestChangeStats({ owner, repo, prNumber, $ }) : null;
1421
+ const changeStats = prNumber ? await getPullRequestChangeStats({ owner, repo, prNumber, $, log }) : null;
1417
1422
 
1418
1423
  // Issue #2132: the summary carries no cost/budget block. `resolvedBudgetStatsData`
1419
1424
  // is computed only so the caller can reuse it for this session's log comment.
package/src/task.mjs CHANGED
@@ -3,6 +3,7 @@
3
3
  import crypto from 'crypto';
4
4
  import path from 'path';
5
5
  import { spawn } from 'child_process';
6
+ import { describeChildExit } from './child-exit.lib.mjs';
6
7
  import { promises as fs } from 'fs';
7
8
  import { buildStartAgentArgs, resolveStartAgentCommand } from './task.agent-command.lib.mjs';
8
9
  import { getDefaultTaskModel, parseTaskArguments } from './task.config.lib.mjs';
@@ -112,8 +113,8 @@ function runCommand(command, args, options = {}) {
112
113
  child.on('error', error => {
113
114
  resolve({ code: 1, stdout, stderr: stderr || error.message });
114
115
  });
115
- child.on('close', code => {
116
- resolve({ code, stdout, stderr });
116
+ child.on('close', (code, signal) => {
117
+ resolve({ code, stdout, stderr, signal });
117
118
  });
118
119
  });
119
120
  }
@@ -122,7 +123,8 @@ async function commandOutput(command, args, options = {}) {
122
123
  const result = await runCommand(command, args, options);
123
124
  if (result.code !== 0) {
124
125
  const output = `${result.stderr || ''}${result.stdout || ''}`.trim();
125
- throw new Error(output || `${command} exited with code ${result.code}`);
126
+ // Issue #2135: `describeChildExit` names a signal instead of "code null".
127
+ throw new Error(output || describeChildExit({ command, code: result.code, signal: result.signal }));
126
128
  }
127
129
  return result.stdout.trim();
128
130
  }
@@ -1248,7 +1248,9 @@ let launchAnnouncementShown = false;
1248
1248
 
1249
1249
  function startSessionMonitoringOnce() {
1250
1250
  if (sessionMonitoringTimer) return;
1251
- sessionMonitoringTimer = startSessionMonitoring(bot, VERBOSE);
1251
+ // Issue #2134: the monitor needs the isolation runner to start a recovery
1252
+ // working session when `--on-session-kill=resume` is in effect.
1253
+ sessionMonitoringTimer = startSessionMonitoring(bot, VERBOSE, 30000, { isolationRunner });
1252
1254
  }
1253
1255
 
1254
1256
  // Issue #1927 (requirements #3/#4): a periodic timestamped heartbeat so the "last
@@ -1,4 +1,5 @@
1
1
  import { spawn } from 'child_process';
2
+ import { describeChildExit } from './child-exit.lib.mjs';
2
3
  import { promisify } from 'util';
3
4
  import { exec as execCallback } from 'child_process';
4
5
  import { t } from './i18n.lib.mjs';
@@ -57,7 +58,7 @@ function executeWithCommand(startScreenCmd, command, args, verbose = false) {
57
58
  });
58
59
  });
59
60
 
60
- child.on('close', code => {
61
+ child.on('close', (code, signal) => {
61
62
  if (code === 0) {
62
63
  resolve({
63
64
  success: true,
@@ -67,7 +68,9 @@ function executeWithCommand(startScreenCmd, command, args, verbose = false) {
67
68
  resolve({
68
69
  success: false,
69
70
  output: stdout,
70
- error: stderr || `Command exited with code ${code}`,
71
+ // Issue #2135: name the signal, so an out-of-memory abort is not
72
+ // reported as "code null".
73
+ error: stderr || describeChildExit({ command: 'Command', code, signal }),
71
74
  });
72
75
  }
73
76
  });