@link-assistant/hive-mind 2.11.3 → 2.11.4
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 +6 -0
- package/package.json +1 -1
- package/src/agent-commander.lib.mjs +33 -12
- package/src/agent-token-usage.lib.mjs +5 -9
- package/src/agent.lib.mjs +110 -152
- package/src/ai-tool-scratch.lib.mjs +143 -0
- package/src/anthropic-cost-accumulator.lib.mjs +36 -0
- package/src/auto-restart-budget.lib.mjs +107 -0
- package/src/auto-restart-exhaustion.lib.mjs +122 -0
- package/src/claude.lib.mjs +19 -15
- package/src/claude.runtime-switch.lib.mjs +4 -4
- package/src/codex.lib.mjs +46 -8
- package/src/credential-sanitization-core.lib.mjs +27 -3
- package/src/formal-ai-pricing.lib.mjs +110 -0
- package/src/gemini.lib.mjs +34 -43
- package/src/github-cost-info.lib.mjs +5 -0
- package/src/json-stream.lib.mjs +219 -0
- package/src/opencode.lib.mjs +56 -74
- package/src/pull-request-changes.lib.mjs +166 -0
- package/src/qwen.lib.mjs +33 -32
- package/src/reviewers-hive.mjs +2 -2
- package/src/solve.auto-merge.lib.mjs +68 -44
- package/src/solve.auto-pr.lib.mjs +11 -3
- package/src/solve.finalize.lib.mjs +15 -0
- package/src/solve.repository.lib.mjs +21 -4
- package/src/solve.restart-shared.lib.mjs +6 -2
- package/src/solve.results.lib.mjs +29 -16
- package/src/solve.watch.lib.mjs +47 -18
- package/src/working-session-summary.lib.mjs +65 -0
- package/src/youtrack/youtrack-sync.mjs +2 -2
|
@@ -67,6 +67,10 @@ const { reportError } = sentryLib;
|
|
|
67
67
|
const prIssueLinking = await import('./pr-issue-linking.lib.mjs');
|
|
68
68
|
const { buildIssueReference, ensureIssueLinkInPullRequestBody } = prIssueLinking;
|
|
69
69
|
|
|
70
|
+
// Issue #2119: the one place that decides whether a pull request changed anything.
|
|
71
|
+
const { formatChangeSummary, getPullRequestChangeStats } = await import('./pull-request-changes.lib.mjs');
|
|
72
|
+
const { buildNoChangesNotice, redactWorkspacePaths } = await import('./working-session-summary.lib.mjs');
|
|
73
|
+
|
|
70
74
|
/**
|
|
71
75
|
* Placeholder patterns used to detect auto-generated PR content that was not updated by the agent.
|
|
72
76
|
* These patterns match the initial WIP PR created by solve.auto-pr.lib.mjs.
|
|
@@ -158,7 +162,7 @@ export const ensurePullRequestIssueLink = async ({ prNumber, issueNumber, owner,
|
|
|
158
162
|
await writeSanitizedPublicationFile(tempBodyFile, linkResult.body);
|
|
159
163
|
|
|
160
164
|
try {
|
|
161
|
-
const updateResult = await command`gh pr edit ${prNumber} --repo ${owner}/${repo} --body-file
|
|
165
|
+
const updateResult = await command`gh pr edit ${prNumber} --repo ${owner}/${repo} --body-file ${tempBodyFile}`;
|
|
162
166
|
await fs.unlink(tempBodyFile).catch(() => {});
|
|
163
167
|
|
|
164
168
|
if (updateResult.code === 0) {
|
|
@@ -787,7 +791,7 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
787
791
|
if (prTitleHasPlaceholder && !argv.autoRestartOnNonUpdatedPullRequestDescription) {
|
|
788
792
|
const updatedTitle = await sanitizeForPublication(pr.title.replace(/^\[WIP\]\s*/, ''));
|
|
789
793
|
await log(` 📝 Removing [WIP] prefix from PR title...`);
|
|
790
|
-
const titleResult = await $`gh pr edit ${pr.number} --repo ${owner}/${repo} --title
|
|
794
|
+
const titleResult = await $`gh pr edit ${pr.number} --repo ${owner}/${repo} --title ${updatedTitle}`;
|
|
791
795
|
if (titleResult.code === 0) {
|
|
792
796
|
await log(` ✅ Updated PR title to: "${updatedTitle}"`);
|
|
793
797
|
} else {
|
|
@@ -801,14 +805,14 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
801
805
|
if (hasPlaceholder && !argv.autoRestartOnNonUpdatedPullRequestDescription) {
|
|
802
806
|
await log(` 📝 Updating PR description to remove placeholder text...`);
|
|
803
807
|
|
|
804
|
-
//
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
808
|
+
// Issue #2119: measure the net diff. The reproduction PRs published
|
|
809
|
+
// "1 file(s) modified, 1 line(s) added" for a pull request that
|
|
810
|
+
// changed nothing, because the stats were never checked for being
|
|
811
|
+
// empty.
|
|
812
|
+
const changeStats = await getPullRequestChangeStats({ owner, repo, prNumber: pr.number, $ });
|
|
813
|
+
if (!changeStats.hasChanges) {
|
|
814
|
+
await log(` ⚠️ PR #${pr.number} has an empty diff - the description will say so instead of claiming changes`, { level: 'warning' });
|
|
815
|
+
}
|
|
812
816
|
|
|
813
817
|
// Get the issue title for context
|
|
814
818
|
const issueTitleResult = await $`gh issue view ${issueNumber} --repo ${owner}/${repo} --json title --jq .title 2>&1`;
|
|
@@ -822,9 +826,7 @@ export const verifyResults = async (owner, repo, branchName, issueNumber, prNumb
|
|
|
822
826
|
This pull request implements a solution for ${issueRef}: ${issueTitle}
|
|
823
827
|
|
|
824
828
|
### Changes
|
|
825
|
-
|
|
826
|
-
- ${additions} line(s) added
|
|
827
|
-
- ${deletions} line(s) removed
|
|
829
|
+
${formatChangeSummary(changeStats)}
|
|
828
830
|
|
|
829
831
|
### Issue Reference
|
|
830
832
|
Fixes ${issueRef}
|
|
@@ -836,7 +838,7 @@ Fixes ${issueRef}
|
|
|
836
838
|
await writeSanitizedPublicationFile(tempBodyFile, newDescription);
|
|
837
839
|
|
|
838
840
|
try {
|
|
839
|
-
const descResult = await $`gh pr edit ${pr.number} --repo ${owner}/${repo} --body-file
|
|
841
|
+
const descResult = await $`gh pr edit ${pr.number} --repo ${owner}/${repo} --body-file ${tempBodyFile}`;
|
|
840
842
|
await fs.unlink(tempBodyFile).catch(() => {});
|
|
841
843
|
|
|
842
844
|
if (descResult.code === 0) {
|
|
@@ -1269,7 +1271,7 @@ export const buildWorkingSessionSummaryDetails = ({ publicPricingEstimate = null
|
|
|
1269
1271
|
return `${costInfo}${budgetStats}`.trim();
|
|
1270
1272
|
};
|
|
1271
1273
|
|
|
1272
|
-
export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumber, owner, repo, publicPricingEstimate = null, anthropicTotalCostUSD = null, pricingInfo = null, budgetStatsData = null }) => {
|
|
1274
|
+
export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumber, owner, repo, publicPricingEstimate = null, anthropicTotalCostUSD = null, pricingInfo = null, budgetStatsData = null, changeStats = null }) => {
|
|
1273
1275
|
if (!resultSummary || typeof resultSummary !== 'string') {
|
|
1274
1276
|
await log('⚠️ No working session summary available to attach', { verbose: true });
|
|
1275
1277
|
return false;
|
|
@@ -1290,10 +1292,16 @@ export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumb
|
|
|
1290
1292
|
pricingInfo,
|
|
1291
1293
|
budgetStatsData,
|
|
1292
1294
|
});
|
|
1295
|
+
// Issue #2119: publish what the session actually produced. The reported
|
|
1296
|
+
// summary said "The `pwd` command completed" and printed the solver's own
|
|
1297
|
+
// /tmp workspace, on a pull request that was still empty.
|
|
1298
|
+
const noChangesNotice = buildNoChangesNotice(changeStats);
|
|
1299
|
+
const summaryBody = redactWorkspacePaths(resultSummary);
|
|
1300
|
+
|
|
1293
1301
|
const comment = `${toolComments.WORKING_SESSION_SUMMARY_AUTOMATION_MARKER}
|
|
1294
1302
|
## ${toolComments.WORKING_SESSION_SUMMARY_MARKER}
|
|
1295
1303
|
|
|
1296
|
-
${
|
|
1304
|
+
${summaryBody}${noChangesNotice ? `\n\n${noChangesNotice}` : ''}${usageDetails ? `\n\n${usageDetails}` : ''}
|
|
1297
1305
|
|
|
1298
1306
|
---
|
|
1299
1307
|
*${toolComments.WORKING_SESSION_SUMMARY_AUTOMATED_FOOTER}*`;
|
|
@@ -1395,6 +1403,10 @@ export const maybeAttachWorkingSessionSummary = async ({ argv, resultSummary, wo
|
|
|
1395
1403
|
...sessionUsage,
|
|
1396
1404
|
})
|
|
1397
1405
|
: null);
|
|
1406
|
+
// Issue #2119: a summary posted on a pull request that changed nothing must
|
|
1407
|
+
// say so, instead of reading as a report of completed work.
|
|
1408
|
+
const changeStats = prNumber ? await getPullRequestChangeStats({ owner, repo, prNumber, $ }) : null;
|
|
1409
|
+
|
|
1398
1410
|
const ok = await attachSolutionSummary({
|
|
1399
1411
|
resultSummary,
|
|
1400
1412
|
prNumber,
|
|
@@ -1405,6 +1417,7 @@ export const maybeAttachWorkingSessionSummary = async ({ argv, resultSummary, wo
|
|
|
1405
1417
|
anthropicTotalCostUSD,
|
|
1406
1418
|
pricingInfo,
|
|
1407
1419
|
budgetStatsData: resolvedBudgetStatsData,
|
|
1420
|
+
changeStats,
|
|
1408
1421
|
});
|
|
1409
1422
|
return { attached: !!ok, reason: ok ? 'attached' : 'post_failed', budgetStatsData: resolvedBudgetStatsData };
|
|
1410
1423
|
};
|
package/src/solve.watch.lib.mjs
CHANGED
|
@@ -46,7 +46,12 @@ const { checkGitHubTerminalState } = terminalStateLib;
|
|
|
46
46
|
|
|
47
47
|
// Issue #1574: Interruptible sleep so CTRL+C is never blocked by a lingering timer
|
|
48
48
|
const { interruptibleSleep } = await import('./interruptible-sleep.lib.mjs');
|
|
49
|
-
|
|
49
|
+
// Issue #2119: one auto-restart budget shared with solve.auto-merge.lib.mjs, so
|
|
50
|
+
// a limit of 5 means 5 AI sessions in total rather than 5 per subsystem, and
|
|
51
|
+
// every label renders in the same `N/M` form.
|
|
52
|
+
const autoRestartBudget = await import('./auto-restart-budget.lib.mjs');
|
|
53
|
+
const { beginAutoRestartBudget, consumeAutoRestartIteration, formatAutoRestartLabel, formatAutoRestartLimit, getAutoRestartIterationsUsed, getRemainingAutoRestartIterations, hasExhaustedAutoRestartBudget } = autoRestartBudget;
|
|
54
|
+
const { failOnAutoRestartBudgetExhausted } = await import('./auto-restart-exhaustion.lib.mjs');
|
|
50
55
|
|
|
51
56
|
// Issue #1625: Central marker constants + tracked comment posting
|
|
52
57
|
const toolComments = await import('./tool-comments.lib.mjs');
|
|
@@ -78,7 +83,9 @@ export const watchForFeedback = async params => {
|
|
|
78
83
|
|
|
79
84
|
const watchInterval = argv.watchInterval || 60; // seconds
|
|
80
85
|
const isTemporaryWatch = argv.temporaryWatch || false;
|
|
81
|
-
|
|
86
|
+
// Issue #2119: claim the shared budget; the same limit is honoured by the
|
|
87
|
+
// auto-merge restart loop that solve.mjs runs afterwards.
|
|
88
|
+
const maxAutoRestartIterations = beginAutoRestartBudget({ maxIterations: argv.autoRestartMaxIterations });
|
|
82
89
|
|
|
83
90
|
// Track latest session data across all iterations for accurate pricing
|
|
84
91
|
// Issue #1056: Seed from the *initial* tool execution so the first auto-restart
|
|
@@ -105,7 +112,7 @@ export const watchForFeedback = async params => {
|
|
|
105
112
|
await log(formatAligned('', 'Monitoring PR:', `#${prNumber}`, 2));
|
|
106
113
|
await log(formatAligned('', 'Mode:', 'Auto-restart (NOT --watch mode)', 2));
|
|
107
114
|
await log(formatAligned('', 'Stop conditions:', 'All changes committed OR PR merged OR max iterations reached', 2));
|
|
108
|
-
await log(formatAligned('', 'Max iterations:',
|
|
115
|
+
await log(formatAligned('', 'Max iterations:', formatAutoRestartLimit(), 2));
|
|
109
116
|
await log(formatAligned('', 'Note:', 'No wait time between iterations in auto-restart mode', 2));
|
|
110
117
|
} else {
|
|
111
118
|
await log(formatAligned('👁️', 'WATCH MODE ACTIVATED', ''));
|
|
@@ -118,8 +125,12 @@ export const watchForFeedback = async params => {
|
|
|
118
125
|
await log('');
|
|
119
126
|
|
|
120
127
|
let iteration = 0;
|
|
121
|
-
|
|
128
|
+
// Issue #2119: mirrors the shared budget counter so every label in this loop
|
|
129
|
+
// reports the run-wide iteration number, not a per-subsystem one.
|
|
130
|
+
let autoRestartCount = getAutoRestartIterationsUsed();
|
|
122
131
|
let firstIterationInTemporaryMode = isTemporaryWatch;
|
|
132
|
+
// Issue #2119: set when the budget runs out, so the caller learns the run failed.
|
|
133
|
+
let budgetExhaustion = null;
|
|
123
134
|
|
|
124
135
|
while (true) {
|
|
125
136
|
iteration++;
|
|
@@ -211,13 +222,25 @@ export const watchForFeedback = async params => {
|
|
|
211
222
|
break;
|
|
212
223
|
}
|
|
213
224
|
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
await
|
|
225
|
+
// Issue #2119: the shared budget is exhausted. Previously this logged a
|
|
226
|
+
// warning and broke out of the loop, leaving the very uncommitted changes
|
|
227
|
+
// that triggered every restart on a temporary clone that is then deleted.
|
|
228
|
+
// Now the run fails and the work is auto-committed first, so the result
|
|
229
|
+
// stays visible in the PR.
|
|
230
|
+
if (hasExhaustedAutoRestartBudget()) {
|
|
231
|
+
const changes = await getUncommittedChangesDetails(tempDir);
|
|
232
|
+
budgetExhaustion = await failOnAutoRestartBudgetExhausted({
|
|
233
|
+
owner,
|
|
234
|
+
repo,
|
|
235
|
+
prNumber,
|
|
236
|
+
tempDir,
|
|
237
|
+
branchName: prBranch || branchName,
|
|
238
|
+
$,
|
|
239
|
+
log,
|
|
240
|
+
formatAligned,
|
|
241
|
+
blocker: changes.length > 0 ? `uncommitted changes remained: ${changes.join(', ')}` : 'uncommitted changes remained',
|
|
242
|
+
subsystem: 'auto-restart on uncommitted changes',
|
|
243
|
+
});
|
|
221
244
|
break;
|
|
222
245
|
}
|
|
223
246
|
}
|
|
@@ -274,17 +297,18 @@ export const watchForFeedback = async params => {
|
|
|
274
297
|
}
|
|
275
298
|
await log('');
|
|
276
299
|
|
|
277
|
-
//
|
|
278
|
-
|
|
300
|
+
// Issue #2119: claim one iteration from the run-wide budget shared with
|
|
301
|
+
// the auto-merge restart loop.
|
|
302
|
+
autoRestartCount = consumeAutoRestartIteration();
|
|
279
303
|
autoRestartIterationsRan = true; // Issue #1290: Mark that auto-restart iterations ran
|
|
280
304
|
lastIterationLogUploaded = false; // Reset log upload tracking for new iteration
|
|
281
|
-
const restartLabel =
|
|
305
|
+
const restartLabel = `Restart ${formatAutoRestartLabel(autoRestartCount)}`;
|
|
282
306
|
await log(formatAligned('🔄', `${restartLabel}:`, `Running ${argv.tool.toUpperCase()} to handle uncommitted changes...`));
|
|
283
307
|
|
|
284
308
|
// Post a comment to PR about auto-restart
|
|
285
309
|
if (prNumber) {
|
|
286
310
|
try {
|
|
287
|
-
const remainingIterations =
|
|
311
|
+
const remainingIterations = getRemainingAutoRestartIterations();
|
|
288
312
|
|
|
289
313
|
// Get uncommitted files list for the comment
|
|
290
314
|
let uncommittedFilesList = '';
|
|
@@ -292,7 +316,7 @@ export const watchForFeedback = async params => {
|
|
|
292
316
|
uncommittedFilesList = '\n\n**Uncommitted files:**\n```\n' + changes.join('\n') + '\n```';
|
|
293
317
|
}
|
|
294
318
|
|
|
295
|
-
const iterationLabel =
|
|
319
|
+
const iterationLabel = formatAutoRestartLabel(autoRestartCount);
|
|
296
320
|
const stopText = remainingIterations === null ? 'Auto-restart is configured with no iteration limit.' : `Auto-restart will stop after changes are committed or discarded, or after ${remainingIterations} more iteration${remainingIterations !== 1 ? 's' : ''}.`;
|
|
297
321
|
const commentBody = `## 🔄 ${AUTO_RESTART_MARKER} ${iterationLabel}\n\nDetected uncommitted changes from previous run. Starting new session to review and commit or discard them.${uncommittedFilesList}\n\n---\n*${stopText} Please wait until working session will end and give your feedback.*`;
|
|
298
322
|
// Issue #1625: Track so this doesn't falsely count as AI-authored.
|
|
@@ -471,7 +495,7 @@ export const watchForFeedback = async params => {
|
|
|
471
495
|
const logFile = getLogFile();
|
|
472
496
|
if (logFile) {
|
|
473
497
|
// Use "Auto-restart X/Y Failure Log" format to distinguish from success logs
|
|
474
|
-
const iterationLabel =
|
|
498
|
+
const iterationLabel = formatAutoRestartLabel(autoRestartCount);
|
|
475
499
|
const customTitle = `⚠️ Auto-restart ${iterationLabel} Failure Log`;
|
|
476
500
|
const logUploadSuccess = await attachLogToGitHub({
|
|
477
501
|
logFile,
|
|
@@ -607,7 +631,7 @@ export const watchForFeedback = async params => {
|
|
|
607
631
|
const logFile = getLogFile();
|
|
608
632
|
if (logFile) {
|
|
609
633
|
// Use "Auto-restart X/Y Log" format as requested in issue #1107
|
|
610
|
-
const iterationLabel =
|
|
634
|
+
const iterationLabel = formatAutoRestartLabel(autoRestartCount);
|
|
611
635
|
const customTitle = `🔄 Auto-restart ${iterationLabel} Log`;
|
|
612
636
|
const logUploadSuccess = await attachLogToGitHub({
|
|
613
637
|
logFile,
|
|
@@ -733,6 +757,11 @@ export const watchForFeedback = async params => {
|
|
|
733
757
|
latestAnthropicCost,
|
|
734
758
|
autoRestartIterationsRan, // True if any auto-restart iterations actually ran
|
|
735
759
|
lastIterationLogUploaded, // True if the last iteration's logs were uploaded
|
|
760
|
+
// Issue #2119: false when the shared auto-restart budget ran out, so the run
|
|
761
|
+
// is reported as failed instead of silently exiting with work still pending.
|
|
762
|
+
success: !budgetExhaustion,
|
|
763
|
+
reason: budgetExhaustion?.reason || null,
|
|
764
|
+
autoRestartLimitReached: Boolean(budgetExhaustion),
|
|
736
765
|
};
|
|
737
766
|
};
|
|
738
767
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Issue #2119: make the published "Working session summary" comment honest.
|
|
5
|
+
*
|
|
6
|
+
* The Kotlin reproduction run ended with the AI tool answering a single `pwd`
|
|
7
|
+
* and returning, and Hive Mind published exactly that as the session's result:
|
|
8
|
+
*
|
|
9
|
+
* <!-- hive-mind:working-session-summary -->
|
|
10
|
+
* ## Working session summary
|
|
11
|
+
*
|
|
12
|
+
* The `pwd` command completed. Output:
|
|
13
|
+
*
|
|
14
|
+
* ```text
|
|
15
|
+
* /tmp/gh-issue-solver-1785421161275
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* (https://github.com/konard/test-hello-world-019fb330-fa49-7c9d-a664-b7ea33bb698a/pull/2#issuecomment-5132013034)
|
|
19
|
+
*
|
|
20
|
+
* Two things are wrong with that comment, and both are Hive Mind's to fix - the
|
|
21
|
+
* tool returning nothing useful is a separate, upstream problem:
|
|
22
|
+
*
|
|
23
|
+
* 1. It reads as a report of completed work. A reader has to open the diff to
|
|
24
|
+
* discover the pull request is still empty. Stating that in the comment
|
|
25
|
+
* turns a misleading summary into an accurate one.
|
|
26
|
+
* 2. It publishes the solver's private workspace path. That path is an
|
|
27
|
+
* implementation detail of the machine the run happened on; it is noise in
|
|
28
|
+
* a public comment and it tells readers about the host filesystem.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/** Solver workspace directories, as created by solve.repository.lib.mjs. */
|
|
32
|
+
const WORKSPACE_PATH_PATTERN = /(?:\/private)?\/(?:tmp|var\/folders\/[^\s/]+\/[^\s/]+\/[^\s/]+)\/gh-issue-solver(?:-resume)?-[A-Za-z0-9._-]+/g;
|
|
33
|
+
|
|
34
|
+
/** Replacement shown in place of a redacted workspace path. */
|
|
35
|
+
export const WORKSPACE_PATH_PLACEHOLDER = '<workspace>';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Replace solver workspace paths with a placeholder.
|
|
39
|
+
*
|
|
40
|
+
* Only the solver's own `gh-issue-solver-*` directories are touched: paths the
|
|
41
|
+
* user actually cares about (repository-relative paths, other absolute paths)
|
|
42
|
+
* are left exactly as the AI wrote them.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} text
|
|
45
|
+
* @returns {string}
|
|
46
|
+
*/
|
|
47
|
+
export const redactWorkspacePaths = text => {
|
|
48
|
+
if (typeof text !== 'string' || !text) return text;
|
|
49
|
+
return text.replace(WORKSPACE_PATH_PATTERN, WORKSPACE_PATH_PLACEHOLDER);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The line appended when the pull request still has an empty diff.
|
|
54
|
+
*
|
|
55
|
+
* @param {{measured: boolean, hasChanges: boolean}|null} changeStats - from
|
|
56
|
+
* `getPullRequestChangeStats`; `null` or unmeasured stats produce no notice,
|
|
57
|
+
* so a failed diff read never turns into a false "no changes" claim.
|
|
58
|
+
* @returns {string} the notice, or an empty string when none applies
|
|
59
|
+
*/
|
|
60
|
+
export const buildNoChangesNotice = changeStats => {
|
|
61
|
+
if (!changeStats || !changeStats.measured || changeStats.hasChanges) return '';
|
|
62
|
+
return '> ⚠️ This pull request still contains no changes - nothing was implemented yet.';
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export default { buildNoChangesNotice, redactWorkspacePaths, WORKSPACE_PATH_PLACEHOLDER };
|
|
@@ -101,7 +101,7 @@ ${youTrackIssue.description || 'No description provided.'}
|
|
|
101
101
|
if (needsUpdate) {
|
|
102
102
|
await log(` 📝 Updating issue #${existingIssue.number} for ${youTrackId}...`);
|
|
103
103
|
|
|
104
|
-
const updateResult = await $`gh issue edit ${existingIssue.number} --repo ${owner}/${repo} --title
|
|
104
|
+
const updateResult = await $`gh issue edit ${existingIssue.number} --repo ${owner}/${repo} --title ${ghTitle} --body ${ghBody}`;
|
|
105
105
|
|
|
106
106
|
if (updateResult.code === 0) {
|
|
107
107
|
await log(` ✅ Updated issue #${existingIssue.number}`);
|
|
@@ -130,7 +130,7 @@ ${youTrackIssue.description || 'No description provided.'}
|
|
|
130
130
|
await log(` ➕ Creating GitHub issue for ${youTrackId}...`);
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
|
-
const createResult = await $`gh issue create --repo ${owner}/${repo} --title
|
|
133
|
+
const createResult = await $`gh issue create --repo ${owner}/${repo} --title ${ghTitle} --body ${ghBody} --label "help wanted"`;
|
|
134
134
|
|
|
135
135
|
if (createResult.code === 0) {
|
|
136
136
|
const issueUrl = createResult.stdout.toString().trim();
|