@link-assistant/hive-mind 2.0.26 ā 2.0.28
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 +12 -0
- package/package.json +1 -1
- package/src/agent.prompts.lib.mjs +6 -0
- package/src/bidirectional-interactive.lib.mjs +15 -0
- package/src/claude.lib.mjs +60 -64
- package/src/claude.prompts.lib.mjs +6 -0
- package/src/codex.lib.mjs +44 -1
- package/src/codex.prompts.lib.mjs +6 -0
- package/src/gemini.prompts.lib.mjs +6 -0
- package/src/locales/en.lino +5 -5
- package/src/locales/hi.lino +5 -5
- package/src/locales/ru.lino +5 -5
- package/src/locales/zh.lino +5 -5
- package/src/opencode.prompts.lib.mjs +6 -1
- package/src/queue-config.lib.mjs +1 -1
- package/src/qwen.prompts.lib.mjs +6 -1
- package/src/session-monitor.lib.mjs +1 -1
- package/src/solve-option-contract.prompts.lib.mjs +19 -0
- package/src/solve.auto-merge.lib.mjs +13 -0
- package/src/solve.auto-pr.lib.mjs +1 -1
- package/src/solve.finalize.lib.mjs +21 -0
- package/src/solve.mjs +11 -19
- package/src/solve.pr-base-command-intervention.lib.mjs +62 -0
- package/src/solve.pr-base-guard.lib.mjs +266 -0
- package/src/solve.restart-shared.lib.mjs +2 -0
- package/src/telegram-bot.mjs +1 -2
- package/src/telegram-solve-queue-command.lib.mjs +12 -17
- package/src/telegram-solve-queue.helpers.lib.mjs +5 -5
- package/src/telegram-solve-queue.lib.mjs +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 2.0.28
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 5b4f3df: Recommend and accept `/queue` instead of the legacy solve-prefixed queue commands, and recommend `/stop` for cancelling running sessions.
|
|
8
|
+
|
|
9
|
+
## 2.0.27
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- d3fdb7b: Respect explicit `--base-branch` through solve sessions by instructing agents not to retarget PRs and restoring the requested PR base before verification or auto-merge handling.
|
|
14
|
+
|
|
3
15
|
## 2.0.26
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -7,6 +7,7 @@ import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mj
|
|
|
7
7
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
8
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
9
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
10
|
+
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Build the user prompt for Agent
|
|
@@ -50,6 +51,11 @@ export const buildUserPrompt = params => {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
const requestedBaseBranchDirective = buildRequestedBaseBranchDirective(argv);
|
|
55
|
+
if (requestedBaseBranchDirective) {
|
|
56
|
+
promptLines.push(requestedBaseBranchDirective);
|
|
57
|
+
}
|
|
58
|
+
|
|
53
59
|
// Add blank line
|
|
54
60
|
promptLines.push('');
|
|
55
61
|
|
|
@@ -790,6 +790,20 @@ export const createBidirectionalHandler = options => {
|
|
|
790
790
|
return writeFrameToStdin(state.claudeStdin, frame, log, verbose);
|
|
791
791
|
};
|
|
792
792
|
|
|
793
|
+
/**
|
|
794
|
+
* Stream a non-comment feedback message into the attached Claude stdin.
|
|
795
|
+
*
|
|
796
|
+
* @param {string} feedbackText
|
|
797
|
+
* @param {Object} [options]
|
|
798
|
+
* @param {string} [options.kind='metadata']
|
|
799
|
+
* @returns {Promise<boolean>} Whether the write succeeded
|
|
800
|
+
*/
|
|
801
|
+
const sendFeedback = async (feedbackText, options = {}) => {
|
|
802
|
+
if (!state.claudeStdin) return false;
|
|
803
|
+
const frame = formatFeedbackForClaude(feedbackText, { kind: options.kind || 'metadata' });
|
|
804
|
+
return writeFrameToStdin(state.claudeStdin, frame, log, verbose);
|
|
805
|
+
};
|
|
806
|
+
|
|
793
807
|
/**
|
|
794
808
|
* Get current handler state (for debugging)
|
|
795
809
|
*
|
|
@@ -828,6 +842,7 @@ export const createBidirectionalHandler = options => {
|
|
|
828
842
|
attachClaudeStdin,
|
|
829
843
|
detachClaudeStdin,
|
|
830
844
|
streamInitialPrompt,
|
|
845
|
+
sendFeedback,
|
|
831
846
|
// Issue #1708: queue mode + status streaming
|
|
832
847
|
markAiBusy,
|
|
833
848
|
markAiIdle,
|
package/src/claude.lib.mjs
CHANGED
|
@@ -34,11 +34,10 @@ import { createThinkingBlockRecovery } from './claude.thinking-block-recovery.li
|
|
|
34
34
|
import { buildMissingClaudeResultMessage, collectClaudeStreamEventFacts, getClaudeMessageContent, shouldFailClaudeStreamWithoutResult } from './claude.stream-events.lib.mjs';
|
|
35
35
|
import { formatNumber, mapModelToId, checkModelVisionCapability } from './claude.model-utils.lib.mjs';
|
|
36
36
|
import { showResumeCommand } from './claude.resume-output.lib.mjs';
|
|
37
|
+
import { createPullRequestBaseBranchCommandIntervention } from './solve.pr-base-command-intervention.lib.mjs';
|
|
37
38
|
export { availableModels, fetchModelInfo }; // Re-export for backward compatibility
|
|
38
39
|
export { formatNumber, mapModelToId, checkModelVisionCapability };
|
|
39
|
-
// Function to validate Claude CLI connection with retry logic
|
|
40
40
|
export const validateClaudeConnection = async (model = 'haiku') => {
|
|
41
|
-
// Map model alias to full ID
|
|
42
41
|
const mappedModel = mapModelToId(model);
|
|
43
42
|
const maxRetries = 3;
|
|
44
43
|
const baseDelay = timeouts.retryBaseDelay;
|
|
@@ -60,14 +59,12 @@ export const validateClaudeConnection = async (model = 'haiku') => {
|
|
|
60
59
|
}
|
|
61
60
|
}
|
|
62
61
|
} catch (versionError) {
|
|
63
|
-
// Version check failed, but we'll continue with the main validation
|
|
64
62
|
if (retryCount === 0) {
|
|
65
63
|
await log(`ā ļø Claude CLI version check failed (${versionError.code}), proceeding with connection test...`);
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
let result;
|
|
69
67
|
try {
|
|
70
|
-
// Primary validation: use printf piping with specified model
|
|
71
68
|
result = await $`printf hi | claude --model ${mappedModel} -p`;
|
|
72
69
|
} catch (pipeError) {
|
|
73
70
|
await log(`ā ļø Pipe validation failed (${pipeError.code}), trying timeout approach...`);
|
|
@@ -109,9 +106,7 @@ export const validateClaudeConnection = async (model = 'haiku') => {
|
|
|
109
106
|
return null;
|
|
110
107
|
};
|
|
111
108
|
const jsonError = checkForJsonError(stdout) || checkForJsonError(stderr);
|
|
112
|
-
// Check for API overload error pattern (Issue #1439: also detect 529 overloaded_error)
|
|
113
109
|
const isOverloadError = (stdout.includes('API Error: 500') && stdout.includes('Overloaded')) || (stdout.includes('API Error: 529') && stdout.includes('Overloaded')) || (stderr.includes('API Error: 500') && stderr.includes('Overloaded')) || (stderr.includes('API Error: 529') && stderr.includes('Overloaded')) || (jsonError && (jsonError.type === 'api_error' || jsonError.type === 'overloaded_error') && jsonError.message === 'Overloaded');
|
|
114
|
-
// Handle overload errors with retry
|
|
115
110
|
if (isOverloadError) {
|
|
116
111
|
if (retryCount < maxRetries) {
|
|
117
112
|
const delay = baseDelay * Math.pow(2, retryCount);
|
|
@@ -187,8 +182,7 @@ export const validateClaudeConnection = async (model = 'haiku') => {
|
|
|
187
182
|
await log(' š” Make sure Claude CLI is installed and accessible', { level: 'error' });
|
|
188
183
|
return false;
|
|
189
184
|
}
|
|
190
|
-
};
|
|
191
|
-
// Start the validation with retry logic
|
|
185
|
+
};
|
|
192
186
|
return await attemptValidation();
|
|
193
187
|
};
|
|
194
188
|
export { handleClaudeRuntimeSwitch }; // Re-export from ./claude.runtime-switch.lib.mjs
|
|
@@ -203,18 +197,15 @@ export const setClaudeVersion = version => {
|
|
|
203
197
|
/** Resolve thinking settings based on --think and --thinking-budget options */
|
|
204
198
|
export const resolveThinkingSettings = async (argv, log) => {
|
|
205
199
|
const minVersion = argv.thinkingBudgetClaudeMinimumVersion || '2.1.12';
|
|
206
|
-
const version = detectedClaudeVersion || '0.0.0';
|
|
200
|
+
const version = detectedClaudeVersion || '0.0.0';
|
|
207
201
|
const isNewVersion = supportsThinkingBudget(version, minVersion);
|
|
208
|
-
// Get max thinking budget from argv or use default (see issue #1146)
|
|
209
202
|
const maxBudget = argv.maxThinkingBudget ?? DEFAULT_MAX_THINKING_BUDGET;
|
|
210
|
-
// Get thinking level mappings calculated from maxBudget
|
|
211
203
|
const thinkingLevelToTokens = getThinkingLevelToTokens(maxBudget);
|
|
212
204
|
const tokensToThinkingLevel = getTokensToThinkingLevel(maxBudget);
|
|
213
205
|
let thinkingBudget = argv.thinkingBudget;
|
|
214
206
|
let thinkLevel = argv.think;
|
|
215
207
|
let translation = null;
|
|
216
208
|
if (isNewVersion) {
|
|
217
|
-
// Claude Code >= 2.1.12: translate --think to --thinking-budget
|
|
218
209
|
if (thinkLevel !== undefined && thinkingBudget === undefined) {
|
|
219
210
|
thinkingBudget = thinkingLevelToTokens[thinkLevel];
|
|
220
211
|
translation = `--think ${thinkLevel} ā --thinking-budget ${thinkingBudget}`;
|
|
@@ -227,7 +218,6 @@ export const resolveThinkingSettings = async (argv, log) => {
|
|
|
227
218
|
}
|
|
228
219
|
}
|
|
229
220
|
} else {
|
|
230
|
-
// Claude Code < 2.1.12: translate --thinking-budget to --think keywords
|
|
231
221
|
if (thinkingBudget !== undefined && thinkLevel === undefined) {
|
|
232
222
|
thinkLevel = tokensToThinkingLevel(thinkingBudget);
|
|
233
223
|
translation = `--thinking-budget ${thinkingBudget} ā --think ${thinkLevel}`;
|
|
@@ -235,7 +225,6 @@ export const resolveThinkingSettings = async (argv, log) => {
|
|
|
235
225
|
await log(`š Translating for Claude Code ${version} (< ${minVersion}):`, { verbose: true });
|
|
236
226
|
await log(` ${translation}`, { verbose: true });
|
|
237
227
|
}
|
|
238
|
-
// Clear thinkingBudget since old versions don't support it
|
|
239
228
|
thinkingBudget = undefined;
|
|
240
229
|
}
|
|
241
230
|
}
|
|
@@ -243,10 +232,8 @@ export const resolveThinkingSettings = async (argv, log) => {
|
|
|
243
232
|
};
|
|
244
233
|
/** Check if Playwright MCP is available and connected to Claude @returns {Promise<boolean>} */
|
|
245
234
|
export const checkPlaywrightMcpAvailability = ensureClaudePlaywrightMcpServer;
|
|
246
|
-
/** Execute Claude with all prompts and settings - main entry point */
|
|
247
235
|
export const executeClaude = async params => {
|
|
248
236
|
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, setLogFile, getLogFile, formatAligned, getResourceSnapshot, claudePath, $ } = params;
|
|
249
|
-
// Check if agent-commander is installed when the option is enabled
|
|
250
237
|
if (argv.promptSubagentsViaAgentCommander) {
|
|
251
238
|
try {
|
|
252
239
|
await $`which start-agent`;
|
|
@@ -256,9 +243,7 @@ export const executeClaude = async params => {
|
|
|
256
243
|
await log('ā ļø agent-commander not installed; prompt guidance will be skipped (npm i -g @link-assistant/agent-commander)');
|
|
257
244
|
}
|
|
258
245
|
}
|
|
259
|
-
// Import prompt building functions from claude.prompts.lib.mjs
|
|
260
246
|
const { buildUserPrompt, buildSystemPrompt } = await import('./claude.prompts.lib.mjs');
|
|
261
|
-
// Check if the model supports vision using models.dev API
|
|
262
247
|
const mappedModel = mapModelToId(argv.model);
|
|
263
248
|
const modelSupportsVision = await checkModelVisionCapability(mappedModel);
|
|
264
249
|
if (argv.verbose) {
|
|
@@ -304,7 +289,6 @@ export const executeClaude = async params => {
|
|
|
304
289
|
if (feedbackLines && feedbackLines.length > 0) {
|
|
305
290
|
await log(' Feedback info: Included', { verbose: true });
|
|
306
291
|
}
|
|
307
|
-
// In dry-run mode, output the actual prompts for debugging
|
|
308
292
|
if (argv.dryRun) {
|
|
309
293
|
await log('\nš User prompt content:', { verbose: true });
|
|
310
294
|
await log('---BEGIN USER PROMPT---', { verbose: true });
|
|
@@ -359,41 +343,30 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
359
343
|
const os = (await use('os')).default;
|
|
360
344
|
const homeDir = options.homeDir || os.homedir();
|
|
361
345
|
const fetchModelInfoForUsage = options.fetchModelInfo || fetchModelInfo;
|
|
362
|
-
// Construct the path to the session JSONL file
|
|
363
|
-
// Format: ~/.claude/projects/<project-dir>/<session-id>.jsonl
|
|
364
|
-
// The project directory name is the full path with slashes replaced by dashes
|
|
365
|
-
// e.g., /tmp/gh-issue-solver-123 becomes -tmp-gh-issue-solver-123
|
|
366
346
|
const projectDirName = tempDir.replace(/\//g, '-');
|
|
367
347
|
const sessionFile = path.join(homeDir, '.claude', 'projects', projectDirName, `${sessionId}.jsonl`);
|
|
368
348
|
try {
|
|
369
349
|
await fs.access(sessionFile);
|
|
370
350
|
} catch {
|
|
371
|
-
// File doesn't exist yet or can't be accessed
|
|
372
351
|
return null;
|
|
373
352
|
}
|
|
374
|
-
// Initialize per-model usage tracking
|
|
375
353
|
const modelUsage = {};
|
|
376
354
|
// Issue #1501: Deduplicate JSONL entries by message ID (stream-json splits responses)
|
|
377
355
|
const seenMessageIds = new Set();
|
|
378
356
|
let duplicateCount = 0;
|
|
379
|
-
// Issue #1501: Track peak context usage per request (not cumulative)
|
|
380
357
|
const peakContextByModel = {};
|
|
381
358
|
let globalPeakContext = 0;
|
|
382
|
-
// Issue #1491: Track sub-sessions between compactification events
|
|
383
359
|
const subSessions = [];
|
|
384
360
|
let currentSubSession = createEmptySubSessionUsage();
|
|
385
361
|
const compactifications = [];
|
|
386
362
|
try {
|
|
387
|
-
// Read the entire file
|
|
388
363
|
const fileContent = await fs.readFile(sessionFile, 'utf8');
|
|
389
364
|
const lines = fileContent.trim().split('\n');
|
|
390
365
|
for (const line of lines) {
|
|
391
366
|
if (!line.trim()) continue;
|
|
392
367
|
try {
|
|
393
368
|
const entry = JSON.parse(line);
|
|
394
|
-
// Issue #1491: Detect compactification boundary events
|
|
395
369
|
if (entry.type === 'system' && entry.subtype === 'compact_boundary') {
|
|
396
|
-
// Save current sub-session and start a new one
|
|
397
370
|
if (currentSubSession.messageCount > 0) {
|
|
398
371
|
subSessions.push(currentSubSession);
|
|
399
372
|
}
|
|
@@ -411,7 +384,7 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
411
384
|
if (msgId) {
|
|
412
385
|
if (seenMessageIds.has(msgId)) {
|
|
413
386
|
duplicateCount++;
|
|
414
|
-
continue;
|
|
387
|
+
continue;
|
|
415
388
|
}
|
|
416
389
|
seenMessageIds.add(msgId);
|
|
417
390
|
}
|
|
@@ -429,7 +402,6 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
429
402
|
if (requestContext > globalPeakContext) {
|
|
430
403
|
globalPeakContext = requestContext;
|
|
431
404
|
}
|
|
432
|
-
// Issue #1491: Also track per-sub-session usage
|
|
433
405
|
if (usage.input_tokens) currentSubSession.inputTokens += usage.input_tokens;
|
|
434
406
|
if (usage.cache_creation_input_tokens) currentSubSession.cacheCreationTokens += usage.cache_creation_input_tokens;
|
|
435
407
|
if (usage.cache_read_input_tokens) currentSubSession.cacheReadTokens += usage.cache_read_input_tokens;
|
|
@@ -448,16 +420,13 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
448
420
|
continue;
|
|
449
421
|
}
|
|
450
422
|
}
|
|
451
|
-
// Push the final sub-session
|
|
452
423
|
if (currentSubSession.messageCount > 0) {
|
|
453
424
|
subSessions.push(currentSubSession);
|
|
454
425
|
}
|
|
455
426
|
mergeResultModelUsage(modelUsage, resultModelUsage);
|
|
456
|
-
// If no usage data was found, return null
|
|
457
427
|
if (Object.keys(modelUsage).length === 0) {
|
|
458
428
|
return null;
|
|
459
429
|
}
|
|
460
|
-
// Fetch model information for each model
|
|
461
430
|
const modelInfoPromises = Object.keys(modelUsage).map(async modelId => {
|
|
462
431
|
const modelInfo = await fetchModelInfoForUsage(modelId);
|
|
463
432
|
return { modelId, modelInfo };
|
|
@@ -469,7 +438,6 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
469
438
|
modelInfoMap[modelId] = modelInfo;
|
|
470
439
|
}
|
|
471
440
|
}
|
|
472
|
-
// Calculate cost for each model and store all characteristics
|
|
473
441
|
for (const [modelId, usage] of Object.entries(modelUsage)) {
|
|
474
442
|
const modelInfo = modelInfoMap[modelId];
|
|
475
443
|
// Issue #1501: Attach peak context usage per model
|
|
@@ -480,7 +448,7 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
480
448
|
usage.costUSD = costData.total;
|
|
481
449
|
usage.costBreakdown = costData.breakdown;
|
|
482
450
|
usage.modelName = modelInfo.name || modelId;
|
|
483
|
-
usage.modelInfo = modelInfo;
|
|
451
|
+
usage.modelInfo = modelInfo;
|
|
484
452
|
} else {
|
|
485
453
|
usage.costUSD = usage._resultCostUSD ?? null;
|
|
486
454
|
usage.costBreakdown = null;
|
|
@@ -491,7 +459,6 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
491
459
|
usage.modelInfo = ctx || out ? { limit: { context: ctx || null, output: out || null } } : null;
|
|
492
460
|
}
|
|
493
461
|
}
|
|
494
|
-
// Calculate grand totals across all models
|
|
495
462
|
let totalInputTokens = 0;
|
|
496
463
|
let totalCacheCreationTokens = 0;
|
|
497
464
|
let totalCacheReadTokens = 0;
|
|
@@ -508,12 +475,9 @@ export const calculateSessionTokens = async (sessionId, tempDir, resultModelUsag
|
|
|
508
475
|
hasCostData = true;
|
|
509
476
|
}
|
|
510
477
|
}
|
|
511
|
-
// Calculate total tokens (input + cache_creation + output, cache_read doesn't count as new tokens)
|
|
512
478
|
const totalTokens = totalInputTokens + totalCacheCreationTokens + totalOutputTokens;
|
|
513
479
|
return {
|
|
514
|
-
// Per-model breakdown
|
|
515
480
|
modelUsage,
|
|
516
|
-
// Grand totals
|
|
517
481
|
inputTokens: totalInputTokens,
|
|
518
482
|
cacheCreationTokens: totalCacheCreationTokens,
|
|
519
483
|
cacheReadTokens: totalCacheReadTokens,
|
|
@@ -540,7 +504,6 @@ export const executeClaudeCommand = async params => {
|
|
|
540
504
|
branchName,
|
|
541
505
|
prompt,
|
|
542
506
|
systemPrompt,
|
|
543
|
-
escapedPrompt,
|
|
544
507
|
escapedSystemPrompt,
|
|
545
508
|
argv,
|
|
546
509
|
log,
|
|
@@ -560,22 +523,19 @@ export const executeClaudeCommand = async params => {
|
|
|
560
523
|
// and issue body/title polling in setupBidirectionalHandler.
|
|
561
524
|
issueNumber,
|
|
562
525
|
} = params;
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
// argv.excludeAllOwnIncommingCommentsFromInput when --bidirectional-interactive-mode is set.
|
|
526
|
+
const expectedBaseBranch = String(argv?.baseBranch || '').trim();
|
|
527
|
+
const escapePromptForShell = promptText => String(promptText).replace(/"/g, '\\"').replace(/\$/g, '\\$');
|
|
566
528
|
await validateBidirectionalModeConfig(argv, log);
|
|
567
|
-
// Issue #1331: Unified retry configuration for all transient API errors
|
|
568
|
-
// (Overloaded, 503 Network Error, Internal Server Error) - same params, all with session preservation
|
|
569
529
|
let retryCount = 0;
|
|
530
|
+
let baseBranchInterventionPrompt = null;
|
|
531
|
+
let baseBranchInterventionResumeCount = 0;
|
|
570
532
|
// Issue #1834 (PR #1835 feedback): corrupted-thinking-block recovery ā resume the session first,
|
|
571
533
|
// then escalate to a fresh restart, auto-committing uncommitted work before each attempt. Created
|
|
572
534
|
// once so its resume/restart caps persist across recursive retry calls.
|
|
573
535
|
const tryThinkingBlockRecovery = createThinkingBlockRecovery({ argv, tempDir, branchName, $, log });
|
|
574
|
-
// Helper `waitWithCountdown` (per-minute countdown for delays >1 minute, Issue #1331) is shared
|
|
575
|
-
// from tool-retry.lib.mjs so claude/codex/gemini/qwen/opencode all use one implementation.
|
|
576
|
-
// Function to execute with retry logic
|
|
577
536
|
const executeWithRetry = async () => {
|
|
578
|
-
|
|
537
|
+
const promptForAttempt = baseBranchInterventionPrompt ? `${prompt}\n\n${baseBranchInterventionPrompt}\n` : prompt;
|
|
538
|
+
const escapedPromptForAttempt = escapePromptForShell(promptForAttempt);
|
|
579
539
|
if (retryCount === 0) {
|
|
580
540
|
await log(`\n${formatAligned('š¤', 'Executing Claude:', argv.model.toUpperCase())}`);
|
|
581
541
|
} else {
|
|
@@ -585,7 +545,7 @@ export const executeClaudeCommand = async params => {
|
|
|
585
545
|
// Issue #1949: logExecutionContext shows the requested alias with its resolved
|
|
586
546
|
// full ID (e.g. "opus (claude-opus-4-8)"). The old `argv.model === 'opus' ?
|
|
587
547
|
// 'opus' : 'sonnet'` heuristic mislabelled every non-"opus" alias as "sonnet".
|
|
588
|
-
await logExecutionContext({ log, model: argv.model, tool: 'claude', tempDir, branchName, promptLength:
|
|
548
|
+
await logExecutionContext({ log, model: argv.model, tool: 'claude', tempDir, branchName, promptLength: promptForAttempt.length, systemPromptLength: systemPrompt.length, feedbackLines });
|
|
589
549
|
}
|
|
590
550
|
const resourcesBefore = await getResourceSnapshot();
|
|
591
551
|
await log('š System resources before execution:', { verbose: true });
|
|
@@ -628,7 +588,6 @@ export const executeClaudeCommand = async params => {
|
|
|
628
588
|
outputTokens: 0,
|
|
629
589
|
eventCount: 0,
|
|
630
590
|
};
|
|
631
|
-
// Create interactive mode handler if enabled
|
|
632
591
|
let interactiveHandler = null;
|
|
633
592
|
if (argv.interactiveMode && owner && repo && prNumber) {
|
|
634
593
|
await log('š Interactive mode: Creating handler for real-time PR comments', { verbose: true });
|
|
@@ -649,10 +608,6 @@ export const executeClaudeCommand = async params => {
|
|
|
649
608
|
} else if (argv.interactiveMode) {
|
|
650
609
|
await log('ā ļø Interactive mode: Disabled - missing PR info (owner/repo/prNumber)', { verbose: true });
|
|
651
610
|
}
|
|
652
|
-
// Issue #817 / #1708: Set up bidirectional handler when --accept-incomming-comments-as-input
|
|
653
|
-
// (or composite --bidirectional-interactive-mode / --auto-input-until-mergeable) is enabled.
|
|
654
|
-
// Returns null when inactive. issueNumber + tempDir are forwarded so the handler can
|
|
655
|
-
// poll issue title/body changes and uncommitted changes during the session (Issue #1708).
|
|
656
611
|
const bidirectionalHandler = await setupBidirectionalHandler({ argv, owner, repo, prNumber, issueNumber, tempDir, $, log });
|
|
657
612
|
const progressMonitor = await initProgressMonitoring(argv, { owner, repo, prNumber, $, log }); // works with or without --interactive-mode
|
|
658
613
|
let execCommand;
|
|
@@ -668,7 +623,6 @@ export const executeClaudeCommand = async params => {
|
|
|
668
623
|
const useClaudeFallbackModel = !resolvedPlanModel && mappedFallbackModel && mappedFallbackModel !== effectiveModel;
|
|
669
624
|
let claudeArgs = `--output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel}`;
|
|
670
625
|
if (useClaudeFallbackModel) claudeArgs += ` --fallback-model ${mappedFallbackModel}`;
|
|
671
|
-
// Declare queuedFeedback for use in catch/finally blocks and return value
|
|
672
626
|
let queuedFeedback = [];
|
|
673
627
|
// Issue #817: When --accept-incomming-comments-as-input is set and we are
|
|
674
628
|
// not resuming a prior session, drive Claude via NDJSON stream-json input
|
|
@@ -692,19 +646,18 @@ export const executeClaudeCommand = async params => {
|
|
|
692
646
|
// Prompt is delivered as the first NDJSON frame on stdin (not as -p).
|
|
693
647
|
claudeArgs += ` -p --input-format stream-json --append-system-prompt "${escapedSystemPrompt}"`;
|
|
694
648
|
} else {
|
|
695
|
-
claudeArgs += ` -p "${
|
|
649
|
+
claudeArgs += ` -p "${escapedPromptForAttempt}" --append-system-prompt "${escapedSystemPrompt}"`;
|
|
696
650
|
}
|
|
697
651
|
const fullCommand = `(cd "${tempDir}" && ${claudePath} ${claudeArgs} | jq -c .)`;
|
|
698
652
|
await log(`\n${formatAligned('š', 'Raw command:', '')}`);
|
|
699
653
|
await log(`${fullCommand}`);
|
|
700
654
|
await log('');
|
|
701
655
|
if (argv.verbose) {
|
|
702
|
-
await log(`š User prompt:\n---BEGIN USER PROMPT---\n${
|
|
656
|
+
await log(`š User prompt:\n---BEGIN USER PROMPT---\n${promptForAttempt}\n---END USER PROMPT---`, { verbose: true });
|
|
703
657
|
await log(`š System prompt:\n---BEGIN SYSTEM PROMPT---\n${systemPrompt}\n---END SYSTEM PROMPT---`, { verbose: true });
|
|
704
658
|
}
|
|
705
659
|
try {
|
|
706
660
|
const { thinkingBudget: resolvedThinkingBudget, thinkLevel, isNewVersion, maxBudget } = await resolveThinkingSettings(argv, log);
|
|
707
|
-
// Issue #1706: --sub-session-size + --disable-1m-context. Resolve here, then pass into getClaudeEnv along with the rest.
|
|
708
661
|
const { parsed: parsedSubSessionSize, contextWindowTokens } = await resolveSubSessionSize({ rawValue: argv.subSessionSize, tool: 'claude', modelId: effectiveModel, fetchModelInfo, log });
|
|
709
662
|
// Issue #817: streaming mode sets exitAfterStopDelayMs=60000 so the headless Claude process stays alive between NDJSON turns.
|
|
710
663
|
const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel, subAgentModel: resolvedSubAgentModel, showThinkingContent: argv.showThinkingContent, exitAfterStopDelayMs: streamingInput ? 60_000 : undefined, disable1mContext: !!argv.disable1mContext, subSessionSize: parsedSubSessionSize, contextWindowTokens });
|
|
@@ -728,7 +681,7 @@ export const executeClaudeCommand = async params => {
|
|
|
728
681
|
const fallbackModelArgs = useClaudeFallbackModel ? ['--fallback-model', mappedFallbackModel] : []; // Issue #1949: Claude Code's per-request overload fallback
|
|
729
682
|
if (useClaudeFallbackModel && argv.verbose) await log(`š Claude --fallback-model: ${mappedFallbackModel} (Issue #1949 ā primary --model ${effectiveModel} stays stable across overload retries)`, { verbose: true });
|
|
730
683
|
if (argv.resume) {
|
|
731
|
-
const simpleEscapedPrompt =
|
|
684
|
+
const simpleEscapedPrompt = promptForAttempt.replace(/"/g, '\\"');
|
|
732
685
|
execCommand = $({ cwd: tempDir, mirror: false, env: claudeEnv })`${claudePath} --resume ${argv.resume} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} ${fallbackModelArgs} ${mcpDisableArgs} ${disallowedToolsArgs} -p "${simpleEscapedPrompt}" --append-system-prompt "${simpleEscapedSystem}"`;
|
|
733
686
|
} else if (streamingInput) {
|
|
734
687
|
// Issue #817: Drive Claude via --input-format stream-json on a pipe
|
|
@@ -737,10 +690,10 @@ export const executeClaudeCommand = async params => {
|
|
|
737
690
|
const streamingInputArgs = ['-p', '--input-format', 'stream-json'];
|
|
738
691
|
execCommand = $({ cwd: tempDir, stdin: 'pipe', mirror: false, env: claudeEnv })`${claudePath} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} ${fallbackModelArgs} ${mcpDisableArgs} ${disallowedToolsArgs} ${streamingInputArgs} --append-system-prompt "${simpleEscapedSystem}"`;
|
|
739
692
|
} else {
|
|
740
|
-
execCommand = $({ cwd: tempDir, stdin:
|
|
693
|
+
execCommand = $({ cwd: tempDir, stdin: promptForAttempt, mirror: false, env: claudeEnv })`${claudePath} --output-format stream-json --verbose --dangerously-skip-permissions --model ${effectiveModel} ${fallbackModelArgs} ${mcpDisableArgs} ${disallowedToolsArgs} --append-system-prompt "${simpleEscapedSystem}"`;
|
|
741
694
|
}
|
|
742
695
|
if (streamingInput) {
|
|
743
|
-
await attachStreamingInput(bidirectionalHandler, execCommand,
|
|
696
|
+
await attachStreamingInput(bidirectionalHandler, execCommand, promptForAttempt, log, !!argv.verbose);
|
|
744
697
|
}
|
|
745
698
|
await log(`${formatAligned('š', 'Command details:', '')}`);
|
|
746
699
|
await log(formatAligned('š', 'Working directory:', tempDir, 2));
|
|
@@ -815,6 +768,19 @@ export const executeClaudeCommand = async params => {
|
|
|
815
768
|
activityTimeoutId.unref();
|
|
816
769
|
}
|
|
817
770
|
};
|
|
771
|
+
const baseBranchCommandIntervention = createPullRequestBaseBranchCommandIntervention({
|
|
772
|
+
expectedBaseBranch,
|
|
773
|
+
prNumber,
|
|
774
|
+
log,
|
|
775
|
+
toolLabel: 'Claude',
|
|
776
|
+
sendInput: message => (streamingInput && bidirectionalHandler?.sendFeedback ? bidirectionalHandler.sendFeedback(message, { kind: 'metadata' }) : false),
|
|
777
|
+
stopSession: async () => {
|
|
778
|
+
if (forceExitTriggered || !execCommand?.kill) return false;
|
|
779
|
+
forceExitTriggered = true;
|
|
780
|
+
killProcessTree('SIGTERM');
|
|
781
|
+
return true;
|
|
782
|
+
},
|
|
783
|
+
});
|
|
818
784
|
for await (const chunk of execCommand.stream()) {
|
|
819
785
|
// Issue #1510: Continue processing stream after SIGTERM to capture final output
|
|
820
786
|
// The stream will naturally end when the process exits (SIGTERM) or is force-killed (SIGKILL after 5s)
|
|
@@ -854,6 +820,7 @@ export const executeClaudeCommand = async params => {
|
|
|
854
820
|
}
|
|
855
821
|
}
|
|
856
822
|
await log(JSON.stringify(data, null, 2));
|
|
823
|
+
await baseBranchCommandIntervention.handleStreamEvent(data);
|
|
857
824
|
if (!sessionId && data.session_id) {
|
|
858
825
|
sessionId = data.session_id;
|
|
859
826
|
await log(`š Session ID: ${sessionId}`);
|
|
@@ -1088,6 +1055,7 @@ export const executeClaudeCommand = async params => {
|
|
|
1088
1055
|
try {
|
|
1089
1056
|
const data = sanitizeObjectStrings(JSON.parse(stdoutLineBuffer));
|
|
1090
1057
|
await log(JSON.stringify(data, null, 2));
|
|
1058
|
+
await baseBranchCommandIntervention.handleStreamEvent(data);
|
|
1091
1059
|
const eventFacts = collectClaudeStreamEventFacts(data);
|
|
1092
1060
|
messageCount += eventFacts.messageCountDelta;
|
|
1093
1061
|
toolUseCount += eventFacts.toolUseCountDelta;
|
|
@@ -1167,6 +1135,34 @@ export const executeClaudeCommand = async params => {
|
|
|
1167
1135
|
|
|
1168
1136
|
// Issue #817: Stop bidirectional mode monitoring and collect queued feedback
|
|
1169
1137
|
queuedFeedback = await finalizeBidirectionalHandler(bidirectionalHandler, log);
|
|
1138
|
+
const baseBranchIntervention = baseBranchCommandIntervention.getIntervention();
|
|
1139
|
+
if (baseBranchIntervention && !baseBranchCommandIntervention.wasSent()) {
|
|
1140
|
+
if ((sessionId || argv.resume) && baseBranchInterventionResumeCount < 1) {
|
|
1141
|
+
argv.resume = sessionId || argv.resume;
|
|
1142
|
+
baseBranchInterventionPrompt = baseBranchIntervention.message;
|
|
1143
|
+
baseBranchInterventionResumeCount++;
|
|
1144
|
+
await log('\nš Resuming Claude with requested base-branch correction prompt...');
|
|
1145
|
+
return await executeWithRetry();
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
return {
|
|
1149
|
+
success: false,
|
|
1150
|
+
sessionId,
|
|
1151
|
+
limitReached,
|
|
1152
|
+
limitResetTime,
|
|
1153
|
+
limitTimezone,
|
|
1154
|
+
messageCount,
|
|
1155
|
+
toolUseCount,
|
|
1156
|
+
errorDuringExecution,
|
|
1157
|
+
anthropicTotalCostUSD,
|
|
1158
|
+
resultSummary,
|
|
1159
|
+
errorInfo: {
|
|
1160
|
+
message: baseBranchIntervention.message,
|
|
1161
|
+
violation: baseBranchIntervention.violation,
|
|
1162
|
+
},
|
|
1163
|
+
queuedFeedback,
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1170
1166
|
const retryableLastError = classifyRetryableError(lastMessage);
|
|
1171
1167
|
// Issue #1834: Corrupted extended-thinking blocks ā try to resume the session first, then fall
|
|
1172
1168
|
// back to a fresh restart (PR #1835 feedback). When both caps are reached, tryThinkingBlockRecovery
|
|
@@ -9,6 +9,7 @@ import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.
|
|
|
9
9
|
import { primaryModelNames } from './models/index.mjs';
|
|
10
10
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
11
11
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
12
|
+
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Build the user prompt for Claude
|
|
@@ -57,6 +58,11 @@ export const buildUserPrompt = params => {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
const requestedBaseBranchDirective = buildRequestedBaseBranchDirective(argv);
|
|
62
|
+
if (requestedBaseBranchDirective) {
|
|
63
|
+
promptLines.push(requestedBaseBranchDirective);
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
// Add contributing guidelines if available
|
|
61
67
|
if (contributingGuidelines) {
|
|
62
68
|
promptLines.push('');
|
package/src/codex.lib.mjs
CHANGED
|
@@ -35,6 +35,7 @@ import { classifyRetryableError, getRetryDelayMs, maybeSwitchToFallbackModel, wa
|
|
|
35
35
|
import { parseSubSessionSize, buildCodexSubSessionSizeConfigArgs, buildCodexDisable1mContextConfigArgs } from './sub-session-size.lib.mjs'; // Issue #1706
|
|
36
36
|
import { getCumulativeContextInputTokens } from './context-fill.lib.mjs';
|
|
37
37
|
import { deployHandoffSkill } from './handoff-skill.lib.mjs'; // Issue #1877
|
|
38
|
+
import { createPullRequestBaseBranchCommandIntervention } from './solve.pr-base-command-intervention.lib.mjs';
|
|
38
39
|
import Decimal from 'decimal.js-light';
|
|
39
40
|
|
|
40
41
|
const CODEX_USAGE_FIELD_NAMES = ['input_tokens', 'cached_input_tokens', 'output_tokens', 'cache_write_tokens', 'cache_creation_input_tokens', 'reasoning_tokens', 'reasoning_output_tokens', 'input_tokens_details.cached_tokens', 'input_tokens_details.cache_read_tokens', 'input_tokens_details.cache_write_tokens', 'input_tokens_details.cache_creation_tokens', 'input_tokens_details.cache_creation_input_tokens', 'output_tokens_details.reasoning_tokens'];
|
|
@@ -767,9 +768,12 @@ export const executeCodexCommand = async params => {
|
|
|
767
768
|
const { tempDir, branchName, prompt, systemPrompt, argv, log, formatAligned, getResourceSnapshot, forkedRepo, feedbackLines, codexPath, $, owner, repo, prNumber, calculatePricing = calculateCodexPricing, waitForRetryDelay = waitWithCountdown } = params;
|
|
768
769
|
|
|
769
770
|
const shellQuote = value => `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
771
|
+
const expectedBaseBranch = String(argv?.baseBranch || '').trim();
|
|
770
772
|
|
|
771
773
|
// Retry configuration
|
|
772
774
|
let retryCount = 0;
|
|
775
|
+
let baseBranchInterventionPrompt = null;
|
|
776
|
+
let baseBranchInterventionResumeCount = 0;
|
|
773
777
|
|
|
774
778
|
const executeWithRetry = async () => {
|
|
775
779
|
// Execute codex command from the cloned repository directory
|
|
@@ -806,7 +810,8 @@ export const executeCodexCommand = async params => {
|
|
|
806
810
|
|
|
807
811
|
// For Codex, we combine system and user prompts into a single message
|
|
808
812
|
// Codex doesn't have separate system prompt support in CLI mode
|
|
809
|
-
const
|
|
813
|
+
const promptForAttempt = baseBranchInterventionPrompt ? `${prompt}\n\n${baseBranchInterventionPrompt}\n` : prompt;
|
|
814
|
+
const combinedPrompt = systemPrompt ? `${systemPrompt}\n\n${promptForAttempt}` : promptForAttempt;
|
|
810
815
|
|
|
811
816
|
// Write the combined prompt to a file for piping
|
|
812
817
|
// Use OS temporary directory instead of repository workspace to avoid polluting the repo
|
|
@@ -919,6 +924,17 @@ export const executeCodexCommand = async params => {
|
|
|
919
924
|
let lastMessage = '';
|
|
920
925
|
let lastTextContent = ''; // Issue #1263: Track last text content for result summary
|
|
921
926
|
let authError = false;
|
|
927
|
+
const baseBranchCommandIntervention = createPullRequestBaseBranchCommandIntervention({
|
|
928
|
+
expectedBaseBranch,
|
|
929
|
+
prNumber,
|
|
930
|
+
log,
|
|
931
|
+
toolLabel: 'Codex',
|
|
932
|
+
stopSession: async () => {
|
|
933
|
+
if (!execCommand?.kill) return false;
|
|
934
|
+
execCommand.kill('SIGTERM');
|
|
935
|
+
return true;
|
|
936
|
+
},
|
|
937
|
+
});
|
|
922
938
|
let codexJsonState = {
|
|
923
939
|
sessionId: null,
|
|
924
940
|
authError: false,
|
|
@@ -949,6 +965,7 @@ export const executeCodexCommand = async params => {
|
|
|
949
965
|
lastMessage = output;
|
|
950
966
|
|
|
951
967
|
codexJsonState = parseCodexExecJsonOutput(output, codexJsonState, mappedModel);
|
|
968
|
+
await baseBranchCommandIntervention.handleCommandExecutions(codexJsonState.commandExecutions);
|
|
952
969
|
|
|
953
970
|
if (interactiveHandler || progressMonitor) {
|
|
954
971
|
for (const rawLine of output.split('\n')) {
|
|
@@ -990,6 +1007,7 @@ export const executeCodexCommand = async params => {
|
|
|
990
1007
|
await log(errorOutput, { stream: 'stderr' });
|
|
991
1008
|
}
|
|
992
1009
|
codexJsonState = parseCodexExecJsonOutput(errorOutput, codexJsonState, mappedModel);
|
|
1010
|
+
await baseBranchCommandIntervention.handleCommandExecutions(codexJsonState.commandExecutions);
|
|
993
1011
|
} else if (chunk.type === 'exit') {
|
|
994
1012
|
exitCode = chunk.code;
|
|
995
1013
|
}
|
|
@@ -1064,6 +1082,31 @@ export const executeCodexCommand = async params => {
|
|
|
1064
1082
|
await log(`š¤ Codex exec JSON did not expose model IDs; using requested model for reporting: ${mappedModel}`, { verbose: true });
|
|
1065
1083
|
}
|
|
1066
1084
|
|
|
1085
|
+
const baseBranchIntervention = baseBranchCommandIntervention.getIntervention();
|
|
1086
|
+
if (baseBranchIntervention) {
|
|
1087
|
+
if ((sessionId || argv.resume) && baseBranchInterventionResumeCount < 1) {
|
|
1088
|
+
argv.resume = sessionId || argv.resume;
|
|
1089
|
+
baseBranchInterventionPrompt = baseBranchIntervention.message;
|
|
1090
|
+
baseBranchInterventionResumeCount++;
|
|
1091
|
+
await log('\nš Resuming Codex with requested base-branch correction prompt...');
|
|
1092
|
+
return await executeWithRetry();
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
return {
|
|
1096
|
+
success: false,
|
|
1097
|
+
sessionId,
|
|
1098
|
+
limitReached,
|
|
1099
|
+
limitResetTime,
|
|
1100
|
+
codexJsonDetails: codexJsonState,
|
|
1101
|
+
errorInfo: {
|
|
1102
|
+
message: baseBranchIntervention.message,
|
|
1103
|
+
violation: baseBranchIntervention.violation,
|
|
1104
|
+
},
|
|
1105
|
+
result: baseBranchIntervention.message,
|
|
1106
|
+
resultSummary: lastTextContent || null,
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1067
1110
|
const firstActualModelId = mappedModel;
|
|
1068
1111
|
const pricingInfo = firstActualModelId ? await calculatePricing(firstActualModelId, codexJsonState.tokenUsage.stepCount > 0 ? codexJsonState.tokenUsage : null) : null;
|
|
1069
1112
|
if (pricingInfo?.totalCostUSD !== null && pricingInfo?.totalCostUSD !== undefined) {
|
|
@@ -8,6 +8,7 @@ import { getHandoffSubPrompt } from './handoff.prompts.lib.mjs';
|
|
|
8
8
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
9
9
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
10
10
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
11
|
+
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* Build the user prompt for Codex
|
|
@@ -51,6 +52,11 @@ export const buildUserPrompt = params => {
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
const requestedBaseBranchDirective = buildRequestedBaseBranchDirective(argv);
|
|
56
|
+
if (requestedBaseBranchDirective) {
|
|
57
|
+
promptLines.push(requestedBaseBranchDirective);
|
|
58
|
+
}
|
|
59
|
+
|
|
54
60
|
// Add blank line
|
|
55
61
|
promptLines.push('');
|
|
56
62
|
|
|
@@ -7,6 +7,7 @@ import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mj
|
|
|
7
7
|
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
8
|
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
9
|
import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
10
|
+
import { buildRequestedBaseBranchDirective } from './solve-option-contract.prompts.lib.mjs';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Build the user prompt for Gemini
|
|
@@ -44,6 +45,11 @@ export const buildUserPrompt = params => {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
const requestedBaseBranchDirective = buildRequestedBaseBranchDirective(argv);
|
|
49
|
+
if (requestedBaseBranchDirective) {
|
|
50
|
+
promptLines.push(requestedBaseBranchDirective);
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
promptLines.push('');
|
|
48
54
|
|
|
49
55
|
if (isContinueMode && feedbackLines && feedbackLines.length > 0) {
|