@link-assistant/hive-mind 1.14.2 → 1.15.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c5dad3c: feat: add --auto-restart-on-non-updated-pull-request-description option (Issue #1162)
8
+
9
+ When using `--tool agent` mode, the pull request title and description could remain
10
+ in their initial WIP placeholder state. This adds an opt-in `--auto-restart-on-non-updated-pull-request-description`
11
+ flag that detects placeholder content after agent execution and auto-restarts with a
12
+ short factual hint. Also adds gentle checklist suggestions to agent/opencode/codex prompts
13
+ (excluding Claude, which handles PR updates naturally).
14
+
3
15
  ## 1.14.2
4
16
 
5
17
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.14.2",
3
+ "version": "1.15.0",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -184,6 +184,7 @@ Preparing pull request.
184
184
  - When there is a package with version and GitHub Actions workflows for automatic release, update the version in your pull request to prepare for next release.
185
185
  - When you update existing pr ${prNumber}, use gh pr edit to modify title and description.
186
186
  - When you finalize the pull request:
187
+ check that pull request title and description are updated (the PR may start with a [WIP] prefix and placeholder description that should be replaced with actual title and description of the changes),
187
188
  follow style from merged prs for code, title, and description,
188
189
  make sure no uncommitted changes corresponding to the original requirements are left behind,
189
190
  make sure the default branch is merged to the pull request's branch,
@@ -194,6 +194,7 @@ Preparing pull request.
194
194
  - When you update existing pr ${prNumber}, use gh pr edit to modify title and description.
195
195
  - When you are about to commit or push code, ALWAYS run local CI checks first if they are available in contributing guidelines (like ruff check, mypy, eslint, etc.) to catch errors before pushing.
196
196
  - When you finalize the pull request:
197
+ check that pull request title and description are updated (the PR may start with a [WIP] prefix and placeholder description that should be replaced with actual title and description of the changes),
197
198
  follow style from merged prs for code, title, and description,
198
199
  make sure no uncommitted changes corresponding to the original requirements are left behind,
199
200
  make sure the default branch is merged to the pull request's branch,
@@ -187,6 +187,7 @@ Preparing pull request.
187
187
  - When there is a package with version and GitHub Actions workflows for automatic release, update the version in your pull request to prepare for next release.
188
188
  - When you update existing pr ${prNumber}, use gh pr edit to modify title and description.
189
189
  - When you finalize the pull request:
190
+ check that pull request title and description are updated (the PR may start with a [WIP] prefix and placeholder description that should be replaced with actual title and description of the changes),
190
191
  follow style from merged prs for code, title, and description,
191
192
  make sure no uncommitted changes corresponding to the original requirements are left behind,
192
193
  make sure the default branch is merged to the pull request's branch,
@@ -210,6 +210,11 @@ export const createYargsConfig = yargsInstance => {
210
210
  description: 'Auto-restart until PR becomes mergeable (no iteration limit). Restarts on new comments from non-bot users, CI failures, merge conflicts, or other issues. Does NOT auto-merge.',
211
211
  default: false,
212
212
  })
213
+ .option('auto-restart-on-non-updated-pull-request-description', {
214
+ type: 'boolean',
215
+ description: 'Automatically restart if PR title or description still contains auto-generated placeholder text after agent execution. Restarts with a hint about what was not updated.',
216
+ default: false,
217
+ })
213
218
  .option('continue-only-on-feedback', {
214
219
  type: 'boolean',
215
220
  description: 'Only continue if feedback is detected (works only with pull request link or issue link with --auto-continue)',
package/src/solve.mjs CHANGED
@@ -1192,6 +1192,57 @@ try {
1192
1192
  const verifyResult = await verifyResults(owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, argv, shouldAttachLogs, shouldRestart, sessionId, tempDir, anthropicTotalCostUSD, publicPricingEstimate, pricingInfo, errorDuringExecution, sessionType);
1193
1193
  const logsAlreadyUploaded = verifyResult?.logUploadSuccess || false;
1194
1194
 
1195
+ // Issue #1162: Auto-restart when PR title/description still has placeholder content
1196
+ if (argv.autoRestartOnNonUpdatedPullRequestDescription && (verifyResult?.prTitleHasPlaceholder || verifyResult?.prBodyHasPlaceholder)) {
1197
+ const { buildPRNotUpdatedHint } = results;
1198
+ const hintLines = buildPRNotUpdatedHint(verifyResult.prTitleHasPlaceholder, verifyResult.prBodyHasPlaceholder);
1199
+
1200
+ await log('');
1201
+ await log('šŸ”„ AUTO-RESTART: PR title/description not updated by agent');
1202
+ hintLines.forEach(async line => await log(` ${line}`));
1203
+ await log(' Restarting tool to give agent another chance to update...');
1204
+ await log('');
1205
+
1206
+ // Import executeToolIteration for re-execution
1207
+ const { executeToolIteration } = await import('./solve.restart-shared.lib.mjs');
1208
+
1209
+ // Re-execute tool with hint as feedback lines
1210
+ const restartResult = await executeToolIteration({
1211
+ issueUrl,
1212
+ owner,
1213
+ repo,
1214
+ issueNumber,
1215
+ prNumber,
1216
+ branchName,
1217
+ tempDir,
1218
+ mergeStateStatus,
1219
+ feedbackLines: hintLines,
1220
+ argv: {
1221
+ ...argv,
1222
+ // Disable auto-restart for this iteration to prevent infinite loops
1223
+ autoRestartOnNonUpdatedPullRequestDescription: false,
1224
+ },
1225
+ });
1226
+
1227
+ // Update session data from restart
1228
+ if (restartResult) {
1229
+ if (restartResult.sessionId) sessionId = restartResult.sessionId;
1230
+ if (restartResult.anthropicTotalCostUSD) anthropicTotalCostUSD = restartResult.anthropicTotalCostUSD;
1231
+ if (restartResult.publicPricingEstimate) publicPricingEstimate = restartResult.publicPricingEstimate;
1232
+ if (restartResult.pricingInfo) pricingInfo = restartResult.pricingInfo;
1233
+ }
1234
+
1235
+ // Clean up CLAUDE.md/.gitkeep again after restart
1236
+ await cleanupClaudeFile(tempDir, branchName, null, argv);
1237
+
1238
+ // Re-verify results after restart (without auto-restart flag to prevent recursion)
1239
+ const reVerifyResult = await verifyResults(owner, repo, branchName, issueNumber, prNumber, prUrl, referenceTime, { ...argv, autoRestartOnNonUpdatedPullRequestDescription: false }, shouldAttachLogs, false, sessionId, tempDir, anthropicTotalCostUSD, publicPricingEstimate, pricingInfo, errorDuringExecution, sessionType);
1240
+
1241
+ if (reVerifyResult?.prTitleHasPlaceholder || reVerifyResult?.prBodyHasPlaceholder) {
1242
+ await log('āš ļø PR title/description still not updated after restart');
1243
+ }
1244
+ }
1245
+
1195
1246
  // Start watch mode if enabled OR if we need to handle uncommitted changes
1196
1247
  if (argv.verbose) {
1197
1248
  await log('');
@@ -47,6 +47,51 @@ const { reportError } = sentryLib;
47
47
  const githubLinking = await import('./github-linking.lib.mjs');
48
48
  const { hasGitHubLinkingKeyword } = githubLinking;
49
49
 
50
+ /**
51
+ * Placeholder patterns used to detect auto-generated PR content that was not updated by the agent.
52
+ * These patterns match the initial WIP PR created by solve.auto-pr.lib.mjs.
53
+ */
54
+ export const PR_TITLE_PLACEHOLDER_PREFIX = '[WIP]';
55
+
56
+ export const PR_BODY_PLACEHOLDER_PATTERNS = ['_Details will be added as the solution draft is developed..._', '**Work in Progress** - The AI assistant is currently analyzing and implementing the solution draft.', '### 🚧 Status'];
57
+
58
+ /**
59
+ * Check if PR title still contains auto-generated placeholder content
60
+ * @param {string} title - PR title
61
+ * @returns {boolean} - true if title has placeholder content
62
+ */
63
+ export const hasPRTitlePlaceholder = title => {
64
+ return title && title.startsWith(PR_TITLE_PLACEHOLDER_PREFIX);
65
+ };
66
+
67
+ /**
68
+ * Check if PR body still contains auto-generated placeholder content
69
+ * @param {string} body - PR body
70
+ * @returns {boolean} - true if body has placeholder content
71
+ */
72
+ export const hasPRBodyPlaceholder = body => {
73
+ return body && PR_BODY_PLACEHOLDER_PATTERNS.some(pattern => body.includes(pattern));
74
+ };
75
+
76
+ /**
77
+ * Build a short factual hint for auto-restart when PR title/description was not updated.
78
+ * Uses neutral, fact-stating language (no forcing words).
79
+ * @param {boolean} titleNotUpdated - Whether the PR title still has placeholder
80
+ * @param {boolean} descriptionNotUpdated - Whether the PR description still has placeholder
81
+ * @returns {string[]} - Array of feedback lines to pass as hint to the restarted session
82
+ */
83
+ export const buildPRNotUpdatedHint = (titleNotUpdated, descriptionNotUpdated) => {
84
+ const lines = [];
85
+ if (titleNotUpdated && descriptionNotUpdated) {
86
+ lines.push('Pull request title and description were not updated.');
87
+ } else if (titleNotUpdated) {
88
+ lines.push('Pull request title was not updated.');
89
+ } else if (descriptionNotUpdated) {
90
+ lines.push('Pull request description was not updated.');
91
+ }
92
+ return lines;
93
+ };
94
+
50
95
  /**
51
96
  * Detect the CLAUDE.md or .gitkeep commit hash from branch structure when not available in session
52
97
  * This handles continue mode where the commit hash was lost between sessions
@@ -465,12 +510,17 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
465
510
  await log(` ā„¹ļø PR #${pr.number} was merged during the session`);
466
511
  }
467
512
 
513
+ // Declare placeholder detection variables outside block scopes for use in return value
514
+ let prTitleHasPlaceholder = false;
515
+ let prBodyHasPlaceholder = false;
516
+
468
517
  // Skip PR body update and ready conversion for merged PRs (they can't be edited)
469
518
  if (!isPrMerged) {
470
519
  // Check if PR body has proper issue linking keywords
520
+ let prBody = '';
471
521
  const prBodyResult = await $`gh pr view ${pr.number} --repo ${owner}/${repo} --json body --jq .body`;
472
522
  if (prBodyResult.code === 0) {
473
- const prBody = prBodyResult.stdout.toString();
523
+ prBody = prBodyResult.stdout.toString();
474
524
  const issueRef = argv.fork ? `${owner}/${repo}#${issueNumber}` : `#${issueNumber}`;
475
525
 
476
526
  // Use the new GitHub linking detection library to check for valid keywords
@@ -513,6 +563,80 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
513
563
  }
514
564
  }
515
565
 
566
+ // Issue #1162: Detect if PR title/description still have auto-generated placeholder content
567
+ // Track this before cleanup for --auto-restart-on-non-updated-pull-request-description
568
+ prTitleHasPlaceholder = hasPRTitlePlaceholder(pr.title);
569
+ prBodyHasPlaceholder = hasPRBodyPlaceholder(prBody);
570
+
571
+ // Issue #1162: Remove [WIP] prefix from title if still present
572
+ // Skip cleanup if auto-restart-on-non-updated-pull-request-description is enabled
573
+ // (let the agent handle it on restart instead)
574
+ if (prTitleHasPlaceholder && !argv.autoRestartOnNonUpdatedPullRequestDescription) {
575
+ const updatedTitle = pr.title.replace(/^\[WIP\]\s*/, '');
576
+ await log(` šŸ“ Removing [WIP] prefix from PR title...`);
577
+ const titleResult = await $`gh pr edit ${pr.number} --repo ${owner}/${repo} --title "${updatedTitle}"`;
578
+ if (titleResult.code === 0) {
579
+ await log(` āœ… Updated PR title to: "${updatedTitle}"`);
580
+ } else {
581
+ await log(` āš ļø Could not update PR title: ${titleResult.stderr ? titleResult.stderr.toString().trim() : 'Unknown error'}`);
582
+ }
583
+ }
584
+
585
+ // Issue #1162: Update PR description if still contains placeholder text
586
+ // Skip cleanup if auto-restart-on-non-updated-pull-request-description is enabled
587
+ const hasPlaceholder = prBodyHasPlaceholder;
588
+ if (hasPlaceholder && !argv.autoRestartOnNonUpdatedPullRequestDescription) {
589
+ await log(` šŸ“ Updating PR description to remove placeholder text...`);
590
+
591
+ // Build a summary of the changes from the PR diff
592
+ const diffResult = await $`gh pr diff ${pr.number} --repo ${owner}/${repo} 2>&1`;
593
+ const diffOutput = diffResult.code === 0 ? diffResult.stdout.toString() : '';
594
+
595
+ // Count files changed
596
+ const filesChanged = (diffOutput.match(/^diff --git/gm) || []).length;
597
+ const additions = (diffOutput.match(/^\+[^+]/gm) || []).length;
598
+ const deletions = (diffOutput.match(/^-[^-]/gm) || []).length;
599
+
600
+ // Get the issue title for context
601
+ const issueTitleResult = await $`gh issue view ${issueNumber} --repo ${owner}/${repo} --json title --jq .title 2>&1`;
602
+ const issueTitle = issueTitleResult.code === 0 ? issueTitleResult.stdout.toString().trim() : 'the issue';
603
+
604
+ // Build new description
605
+ const fs = (await use('fs')).promises;
606
+ const issueRef = argv.fork ? `${owner}/${repo}#${issueNumber}` : `#${issueNumber}`;
607
+ const newDescription = `## Summary
608
+
609
+ This pull request implements a solution for ${issueRef}: ${issueTitle}
610
+
611
+ ### Changes
612
+ - ${filesChanged} file(s) modified
613
+ - ${additions} line(s) added
614
+ - ${deletions} line(s) removed
615
+
616
+ ### Issue Reference
617
+ Fixes ${issueRef}
618
+
619
+ ---
620
+ *This PR was created automatically by the AI issue solver*`;
621
+
622
+ const tempBodyFile = `/tmp/pr-body-finalize-${pr.number}-${Date.now()}.md`;
623
+ await fs.writeFile(tempBodyFile, newDescription);
624
+
625
+ try {
626
+ const descResult = await $`gh pr edit ${pr.number} --repo ${owner}/${repo} --body-file "${tempBodyFile}"`;
627
+ await fs.unlink(tempBodyFile).catch(() => {});
628
+
629
+ if (descResult.code === 0) {
630
+ await log(` āœ… Updated PR description with solution summary`);
631
+ } else {
632
+ await log(` āš ļø Could not update PR description: ${descResult.stderr ? descResult.stderr.toString().trim() : 'Unknown error'}`);
633
+ }
634
+ } catch (descError) {
635
+ await fs.unlink(tempBodyFile).catch(() => {});
636
+ await log(` āš ļø Error updating PR description: ${descError.message}`);
637
+ }
638
+ }
639
+
516
640
  // Check if PR is ready for review (convert from draft if necessary)
517
641
  if (pr.isDraft) {
518
642
  await log(' šŸ”„ Converting PR from draft to ready for review...');
@@ -563,11 +687,17 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
563
687
  }
564
688
  await log('\n✨ Please review the pull request for the proposed solution draft.');
565
689
  // Don't exit if watch mode is enabled OR if auto-restart is needed for uncommitted changes
566
- if (!argv.watch && !shouldRestart) {
690
+ // Also don't exit if auto-restart-on-non-updated-pull-request-description detected placeholders
691
+ const shouldAutoRestartForPlaceholder = argv.autoRestartOnNonUpdatedPullRequestDescription && (prTitleHasPlaceholder || prBodyHasPlaceholder);
692
+ if (shouldAutoRestartForPlaceholder) {
693
+ await log('\nšŸ”„ Placeholder detected in PR title/description - auto-restart will be triggered');
694
+ }
695
+ if (!argv.watch && !shouldRestart && !shouldAutoRestartForPlaceholder) {
567
696
  await safeExit(0, 'Process completed successfully');
568
697
  }
569
698
  // Issue #1154: Return logUploadSuccess to prevent duplicate log uploads
570
- return { logUploadSuccess }; // Return for watch mode or auto-restart
699
+ // Issue #1162: Return placeholder detection status for auto-restart
700
+ return { logUploadSuccess, prTitleHasPlaceholder, prBodyHasPlaceholder }; // Return for watch mode or auto-restart
571
701
  } else {
572
702
  await log(` ā„¹ļø Found pull request #${pr.number} but it appears to be from a different session`);
573
703
  }