@link-assistant/hive-mind 1.72.5 → 1.72.6

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.72.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 57f15ec: Detect same-account human feedback in auto-restart comment monitoring only when the AI tool is idle, while still filtering hive-mind tool-generated comments by marker and tracked ID.
8
+
3
9
  ## 1.72.5
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.72.5",
3
+ "version": "1.72.6",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -76,7 +76,7 @@ const formatRunLine = run => {
76
76
  // search scope for checkForExistingComment() stays in lock-step with the
77
77
  // markers actually embedded in tool-posted comments.
78
78
  const toolComments = await import('./tool-comments.lib.mjs');
79
- const { SESSION_ENDING_MARKERS } = toolComments;
79
+ const { SESSION_ENDING_MARKERS, isToolGeneratedComment, isToolTrackedCommentId } = toolComments;
80
80
 
81
81
  /**
82
82
  * Issue #1323: Check if a comment with specific content already exists on the PR
@@ -168,14 +168,25 @@ export const checkForExistingComment = async (owner, repo, prNumber, commentSign
168
168
 
169
169
  /**
170
170
  * Check for new comments from non-bot users since last commit
171
+ *
172
+ * Same-account comments are only considered feedback when
173
+ * `trustAuthenticatedUserComments` is true. Keep the default false for callers
174
+ * that may run while an AI tool is still active: those tools can post through
175
+ * the authenticated GitHub account.
176
+ *
177
+ * @param {Function} commandRunner - Tagged-template command runner, injectable for tests
178
+ * @param {Object} options - Comment classification options
179
+ * @param {boolean} options.trustAuthenticatedUserComments - True only when the caller knows the AI tool is not running
171
180
  * @returns {Promise<{hasNewComments: boolean, comments: Array}>}
172
181
  */
173
- export const checkForNonBotComments = async (owner, repo, prNumber, issueNumber, lastCheckTime, verbose = false) => {
182
+ export const checkForNonBotComments = async (owner, repo, prNumber, issueNumber, lastCheckTime, verbose = false, commandRunner = $, options = {}) => {
174
183
  try {
184
+ const { trustAuthenticatedUserComments = false } = options;
185
+
175
186
  // Get current GitHub user to identify which comments are from the bot/hive-mind
176
187
  let currentUser = null;
177
188
  try {
178
- const userResult = await $`gh api user --jq .login`;
189
+ const userResult = await commandRunner`gh api user --jq .login`;
179
190
  if (userResult.code === 0) {
180
191
  currentUser = userResult.stdout.toString().trim();
181
192
  }
@@ -183,7 +194,12 @@ export const checkForNonBotComments = async (owner, repo, prNumber, issueNumber,
183
194
  // If we can't get the current user, continue without filtering
184
195
  }
185
196
 
186
- // Common bot usernames and patterns to filter out
197
+ // Common bot usernames and patterns to filter out.
198
+ // Issue #1821: In same-account operation, humans and AI tools can both
199
+ // post through the authenticated account. The safe default treats that
200
+ // account as tool-owned; auto-restart-until-mergeable opts in to trusting
201
+ // same-account comments only while no AI tool execution is active, and
202
+ // still filters tool-generated comments by tracked IDs and marker strings.
187
203
  // Note: Patterns use word boundaries or end-of-string to avoid false positives
188
204
  // (e.g., "claudeuser" should NOT match as a bot)
189
205
  const botPatterns = [
@@ -201,21 +217,21 @@ export const checkForNonBotComments = async (owner, repo, prNumber, issueNumber,
201
217
 
202
218
  const isBot = login => {
203
219
  if (!login) return false;
204
- // Check if it's the current user (the bot running hive-mind)
205
- if (currentUser && login === currentUser) return true;
206
220
  // Check against known bot patterns
207
221
  return botPatterns.some(pattern => pattern.test(login));
208
222
  };
209
223
 
224
+ const isToolComment = comment => isToolTrackedCommentId(comment.id) || isToolGeneratedComment(comment.body);
225
+
210
226
  // Fetch PR conversation comments
211
- const prCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate`;
227
+ const prCommentsResult = await commandRunner`gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate`;
212
228
  let prComments = [];
213
229
  if (prCommentsResult.code === 0 && prCommentsResult.stdout) {
214
230
  prComments = JSON.parse(prCommentsResult.stdout.toString() || '[]');
215
231
  }
216
232
 
217
233
  // Fetch PR review comments (inline code comments)
218
- const prReviewCommentsResult = await $`gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate`;
234
+ const prReviewCommentsResult = await commandRunner`gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate`;
219
235
  let prReviewComments = [];
220
236
  if (prReviewCommentsResult.code === 0 && prReviewCommentsResult.stdout) {
221
237
  prReviewComments = JSON.parse(prReviewCommentsResult.stdout.toString() || '[]');
@@ -224,7 +240,7 @@ export const checkForNonBotComments = async (owner, repo, prNumber, issueNumber,
224
240
  // Fetch issue comments if we have an issue number
225
241
  let issueComments = [];
226
242
  if (issueNumber && issueNumber !== prNumber) {
227
- const issueCommentsResult = await $`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
243
+ const issueCommentsResult = await commandRunner`gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate`;
228
244
  if (issueCommentsResult.code === 0 && issueCommentsResult.stdout) {
229
245
  issueComments = JSON.parse(issueCommentsResult.stdout.toString() || '[]');
230
246
  }
@@ -233,14 +249,28 @@ export const checkForNonBotComments = async (owner, repo, prNumber, issueNumber,
233
249
  // Combine all comments
234
250
  const allComments = [...prComments, ...prReviewComments, ...issueComments];
235
251
 
236
- // Filter for new comments from non-bot users
252
+ // Filter for new comments from non-bot users. Automated hive-mind/tool
253
+ // comments are excluded by marker/ID, including comments posted by the
254
+ // authenticated user during the current or a previous process.
237
255
  const newNonBotComments = allComments.filter(comment => {
238
256
  const commentTime = new Date(comment.created_at);
239
257
  const isAfterLastCheck = commentTime > lastCheckTime;
240
- const isFromNonBot = !isBot(comment.user?.login);
241
-
242
- if (verbose && isAfterLastCheck && isFromNonBot) {
243
- console.log(`[VERBOSE] New non-bot comment from ${comment.user?.login} at ${comment.created_at}`);
258
+ const login = comment.user?.login;
259
+ const isFromAuthenticatedUser = Boolean(currentUser && login === currentUser);
260
+ const isFromTool = isToolComment(comment);
261
+ const isFromAuthenticatedUserToolContext = isFromAuthenticatedUser && !trustAuthenticatedUserComments;
262
+ const isFromBot = isBot(login) || isFromAuthenticatedUserToolContext;
263
+ const isFromNonBot = !isFromBot && !isFromTool;
264
+
265
+ if (verbose && isAfterLastCheck && isFromTool) {
266
+ console.log(`[VERBOSE] Skipping tool-generated comment from ${login} at ${comment.created_at}`);
267
+ } else if (verbose && isAfterLastCheck && isFromAuthenticatedUserToolContext) {
268
+ console.log(`[VERBOSE] Skipping authenticated-user comment from ${login} at ${comment.created_at} because same-account feedback is not trusted in this context`);
269
+ } else if (verbose && isAfterLastCheck && isFromBot) {
270
+ console.log(`[VERBOSE] Skipping bot comment from ${login} at ${comment.created_at}`);
271
+ } else if (verbose && isAfterLastCheck && isFromNonBot) {
272
+ const sameAccountSuffix = currentUser && login === currentUser ? ' (authenticated user)' : '';
273
+ console.log(`[VERBOSE] New non-bot comment from ${login}${sameAccountSuffix} at ${comment.created_at}`);
244
274
  }
245
275
 
246
276
  return isAfterLastCheck && isFromNonBot;
@@ -206,8 +206,12 @@ export const watchUntilMergeable = async params => {
206
206
  // Keep the counter as-is (it reached the safety valve or wasn't needed).
207
207
  }
208
208
 
209
- // Check for new comments from non-bot users
210
- const { hasNewComments, comments } = await checkForNonBotComments(owner, repo, prNumber, issueNumber, lastCheckTime, argv.verbose);
209
+ // Check for new comments from non-bot users. At this point the AI tool
210
+ // is not executing, so same-account non-tool comments can be trusted as
211
+ // human feedback while known tool comments remain filtered by markers/IDs.
212
+ const { hasNewComments, comments } = await checkForNonBotComments(owner, repo, prNumber, issueNumber, lastCheckTime, argv.verbose, $, {
213
+ trustAuthenticatedUserComments: true,
214
+ });
211
215
 
212
216
  // Check for uncommitted changes using shared utility
213
217
  const hasUncommittedChanges = await checkForUncommittedChanges(tempDir, argv);