@link-assistant/hive-mind 1.72.0 ā 1.72.1
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 +24 -0
- package/package.json +1 -1
- package/src/claude.command-builder.lib.mjs +38 -0
- package/src/claude.lib.mjs +20 -19
- package/src/codex.lib.mjs +8 -2
- package/src/gemini.lib.mjs +5 -1
- package/src/solve.mjs +26 -27
- package/src/solve.results.lib.mjs +21 -53
- package/src/solve.resume-command.lib.mjs +31 -0
- package/src/usage-limit.lib.mjs +26 -5
- package/CLAUDE.md +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.72.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 249646e: Fix: Move Claude CLI resume command from GitHub comment to logs
|
|
8
|
+
|
|
9
|
+
When usage limit is reached, the GitHub comment now only mentions the
|
|
10
|
+
`--auto-continue-on-limit-reset` option instead of showing bash commands.
|
|
11
|
+
This is more user-friendly for Telegram bot users who don't use CLI commands directly.
|
|
12
|
+
|
|
13
|
+
The Claude CLI resume command is still available in the logs (in the collapsed
|
|
14
|
+
block or gist link), allowing advanced users to resume manually if needed:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
(cd "/tmp/gh-issue-solver-..." && claude --resume session-id)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Changes:
|
|
21
|
+
- GitHub comments now only suggest using the `--auto-continue-on-limit-reset` option
|
|
22
|
+
- Resume commands are kept in logs only (not in the visible comment)
|
|
23
|
+
- Session ID is still shown for reference
|
|
24
|
+
|
|
25
|
+
Fixes #942
|
|
26
|
+
|
|
3
27
|
## 1.72.0
|
|
4
28
|
|
|
5
29
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -52,6 +52,43 @@ export const buildClaudeResumeCommand = ({ tempDir, sessionId, claudePath = 'cla
|
|
|
52
52
|
return `(cd "${tempDir}" && ${claudePath} ${args})`;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Build the Claude CLI autonomous resume command
|
|
57
|
+
*
|
|
58
|
+
* This generates a fully autonomous command that includes all flags needed to run
|
|
59
|
+
* without user interaction. The user can copy-paste this command and it will
|
|
60
|
+
* continue the session autonomously.
|
|
61
|
+
*
|
|
62
|
+
* The command includes:
|
|
63
|
+
* - --resume <sessionId>: Resume from the specified session
|
|
64
|
+
* - --output-format stream-json: For streaming output
|
|
65
|
+
* - --dangerously-skip-permissions: Skip interactive permission prompts
|
|
66
|
+
* - --model <model>: Use the same model as the original session
|
|
67
|
+
* - -p "Continue.": Simple prompt to continue the work
|
|
68
|
+
*
|
|
69
|
+
* Note: This function is specifically designed for Claude CLI (--tool claude)
|
|
70
|
+
* and should only be used when the tool is 'claude' or undefined (defaults to claude).
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} options - Options for building the command
|
|
73
|
+
* @param {string} options.tempDir - The working directory (e.g., /tmp/gh-issue-solver-...)
|
|
74
|
+
* @param {string} options.sessionId - The session ID to resume
|
|
75
|
+
* @param {string} options.claudePath - Path to the claude CLI binary (defaults to 'claude')
|
|
76
|
+
* @param {string} [options.model] - The model to use (e.g., 'sonnet', 'opus', 'claude-sonnet-4-20250514')
|
|
77
|
+
* @returns {string} - The full autonomous resume command
|
|
78
|
+
*/
|
|
79
|
+
export const buildClaudeAutonomousResumeCommand = ({ tempDir, sessionId, claudePath = 'claude', model }) => {
|
|
80
|
+
let args = `--resume ${sessionId} --output-format stream-json --dangerously-skip-permissions`;
|
|
81
|
+
|
|
82
|
+
if (model) {
|
|
83
|
+
args += ` --model ${model}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Add a simple "Continue." prompt to continue the autonomous work
|
|
87
|
+
args += ` -p "Continue."`;
|
|
88
|
+
|
|
89
|
+
return `(cd "${tempDir}" && ${claudePath} ${args})`;
|
|
90
|
+
};
|
|
91
|
+
|
|
55
92
|
/**
|
|
56
93
|
* Build the Claude CLI initial command with the (cd ... && claude ...) pattern
|
|
57
94
|
*
|
|
@@ -85,5 +122,6 @@ export const buildClaudeInitialCommand = ({ tempDir, claudePath = 'claude', mode
|
|
|
85
122
|
// Export default object for compatibility
|
|
86
123
|
export default {
|
|
87
124
|
buildClaudeResumeCommand,
|
|
125
|
+
buildClaudeAutonomousResumeCommand,
|
|
88
126
|
buildClaudeInitialCommand,
|
|
89
127
|
};
|
package/src/claude.lib.mjs
CHANGED
|
@@ -16,7 +16,8 @@ import { initProgressMonitoring } from './solve.progress-monitoring.lib.mjs';
|
|
|
16
16
|
import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
17
17
|
import Decimal from 'decimal.js-light';
|
|
18
18
|
import { displayBudgetStats, createEmptySubSessionUsage, accumulateModelUsage, displayModelUsage, displayCostComparison, mergeResultModelUsage, createSubAgentCallEntry, accumulateSubAgentUsage, getRawRequestInputTokens } from './claude.budget-stats.lib.mjs';
|
|
19
|
-
import { buildClaudeResumeCommand } from './claude.command-builder.lib.mjs';
|
|
19
|
+
import { buildClaudeResumeCommand, buildClaudeAutonomousResumeCommand } from './claude.command-builder.lib.mjs';
|
|
20
|
+
import { buildSolveResumeCommand } from './solve.resume-command.lib.mjs'; // Issue #942
|
|
20
21
|
import { SESSION_FORCE_KILLED_MARKER, postTrackedComment } from './tool-comments.lib.mjs'; // Issue #1625
|
|
21
22
|
import { handleClaudeRuntimeSwitch } from './claude.runtime-switch.lib.mjs'; // see issue #1141
|
|
22
23
|
import { CLAUDE_MODELS as availableModels } from './models/index.mjs'; // Issue #1221
|
|
@@ -27,13 +28,14 @@ import { fetchModelInfo } from './model-info.lib.mjs';
|
|
|
27
28
|
import { classifyRetryableError, maybeSwitchToFallbackModel } from './tool-retry.lib.mjs';
|
|
28
29
|
import { resolveSubSessionSize } from './sub-session-size.lib.mjs'; // Issue #1706
|
|
29
30
|
import { withAgentsMdAsClaudeMd } from './agents-md-claude-support.lib.mjs';
|
|
30
|
-
export { availableModels }; // Re-export for backward compatibility
|
|
31
|
-
|
|
32
|
-
const showResumeCommand = async (sessionId, tempDir, claudePath, model, log) => {
|
|
31
|
+
export { availableModels, fetchModelInfo }; // Re-export for backward compatibility
|
|
32
|
+
const showResumeCommand = async (sessionId, tempDir, claudePath, model, log, argv = null) => {
|
|
33
33
|
if (!sessionId || !tempDir) return;
|
|
34
|
-
|
|
35
|
-
await log(
|
|
36
|
-
await log(` ${
|
|
34
|
+
await log(`\nš” To continue this session:\n`);
|
|
35
|
+
await log(` Interactive mode: ${buildClaudeResumeCommand({ tempDir, sessionId, claudePath, model })}\n`);
|
|
36
|
+
await log(` Autonomous mode: ${buildClaudeAutonomousResumeCommand({ tempDir, sessionId, claudePath, model })}\n`);
|
|
37
|
+
// Issue #942: 3rd option - restart the entire /solve flow, not just the claude session.
|
|
38
|
+
if (argv && argv.url) await log(` Solve resume mode: ${buildSolveResumeCommand({ issueUrl: argv.url, sessionId, tool: argv.tool || 'claude', model: argv.model, fallbackModel: argv.fallbackModel, tempDir })}\n`);
|
|
37
39
|
};
|
|
38
40
|
/** Format numbers with spaces as thousands separator (no commas) */
|
|
39
41
|
export const formatNumber = num => {
|
|
@@ -1238,25 +1240,24 @@ export const executeClaudeCommand = async params => {
|
|
|
1238
1240
|
limitReached = true;
|
|
1239
1241
|
limitResetTime = limitInfo.resetTime;
|
|
1240
1242
|
limitTimezone = limitInfo.timezone;
|
|
1241
|
-
|
|
1242
|
-
//
|
|
1243
|
+
const hasSession = tempDir && sessionId;
|
|
1244
|
+
// Issue #942: include all 3 resume options (interactive/autonomous/solve).
|
|
1243
1245
|
const messageLines = formatUsageLimitMessage({
|
|
1244
1246
|
tool: 'Anthropic Claude Code',
|
|
1245
1247
|
resetTime: limitInfo.resetTime,
|
|
1246
1248
|
sessionId,
|
|
1247
|
-
|
|
1249
|
+
interactiveResumeCommand: hasSession ? buildClaudeResumeCommand({ tempDir, sessionId, model: argv.model }) : null,
|
|
1250
|
+
autonomousResumeCommand: hasSession ? buildClaudeAutonomousResumeCommand({ tempDir, sessionId, model: argv.model }) : null,
|
|
1251
|
+
solveResumeCommand: hasSession && argv?.url ? buildSolveResumeCommand({ issueUrl: argv.url, sessionId, tool: argv.tool || 'claude', model: argv.model, fallbackModel: argv.fallbackModel, tempDir }) : null,
|
|
1248
1252
|
});
|
|
1249
|
-
for (const line of messageLines) {
|
|
1250
|
-
await log(line, { level: 'warning' });
|
|
1251
|
-
}
|
|
1253
|
+
for (const line of messageLines) await log(line, { level: 'warning' });
|
|
1252
1254
|
} else if (lastMessage.includes('context_length_exceeded')) {
|
|
1253
1255
|
await log('\n\nā Context length exceeded. Try with a smaller issue or split the work.', { level: 'error' });
|
|
1254
1256
|
} else {
|
|
1255
1257
|
await log(`\n\nā Claude command failed with exit code ${exitCode}`, { level: 'error' });
|
|
1256
|
-
if (sessionId && !argv.resume) {
|
|
1257
|
-
await log(`š Session ID
|
|
1258
|
-
await
|
|
1259
|
-
await log(` ${process.argv[0]} ${process.argv[1]} ${argv.url} --resume ${sessionId}`);
|
|
1258
|
+
if (sessionId && !argv.resume && tempDir) {
|
|
1259
|
+
await log(`š Session ID: ${sessionId}`);
|
|
1260
|
+
await showResumeCommand(sessionId, tempDir, claudePath, argv.model, log, argv);
|
|
1260
1261
|
}
|
|
1261
1262
|
}
|
|
1262
1263
|
}
|
|
@@ -1275,7 +1276,7 @@ export const executeClaudeCommand = async params => {
|
|
|
1275
1276
|
await log('\nš System resources after execution:', { verbose: true });
|
|
1276
1277
|
await log(` Memory: ${resourcesAfter.memory.split('\n')[1]}`, { verbose: true });
|
|
1277
1278
|
await log(` Load: ${resourcesAfter.load}`, { verbose: true });
|
|
1278
|
-
await showResumeCommand(sessionId, tempDir, claudePath, argv.model, log);
|
|
1279
|
+
await showResumeCommand(sessionId, tempDir, claudePath, argv.model, log, argv);
|
|
1279
1280
|
return {
|
|
1280
1281
|
success: false,
|
|
1281
1282
|
sessionId,
|
|
@@ -1361,7 +1362,7 @@ export const executeClaudeCommand = async params => {
|
|
|
1361
1362
|
await log(` ā ļø Could not calculate token usage: ${tokenError.message}`, { verbose: true });
|
|
1362
1363
|
}
|
|
1363
1364
|
}
|
|
1364
|
-
await showResumeCommand(sessionId, tempDir, claudePath, argv.model, log);
|
|
1365
|
+
await showResumeCommand(sessionId, tempDir, claudePath, argv.model, log, argv);
|
|
1365
1366
|
return {
|
|
1366
1367
|
success: true,
|
|
1367
1368
|
sessionId,
|
package/src/codex.lib.mjs
CHANGED
|
@@ -17,6 +17,8 @@ import { log } from './lib.mjs';
|
|
|
17
17
|
import { reportError } from './sentry.lib.mjs';
|
|
18
18
|
import { timeouts, retryLimits } from './config.lib.mjs';
|
|
19
19
|
import { detectUsageLimit, formatUsageLimitMessage } from './usage-limit.lib.mjs';
|
|
20
|
+
import { buildSolveResumeCommand } from './solve.resume-command.lib.mjs'; // Issue #942
|
|
21
|
+
const __codexBuildSolveResumeCmd = (argv, sessionId, tempDir) => (sessionId && argv?.url ? buildSolveResumeCommand({ issueUrl: argv.url, sessionId, tool: 'codex', model: argv.model, fallbackModel: argv.fallbackModel, tempDir }) : null);
|
|
20
22
|
import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
21
23
|
import { mapModelToId, resolveCodexReasoningEffort } from './codex.options.lib.mjs';
|
|
22
24
|
import { createInteractiveHandler } from './interactive-mode.lib.mjs';
|
|
@@ -1011,11 +1013,13 @@ export const executeCodexCommand = async params => {
|
|
|
1011
1013
|
limitReached = true;
|
|
1012
1014
|
limitResetTime = limitInfo.resetTime;
|
|
1013
1015
|
|
|
1016
|
+
// Issue #942: build proper solve resume command (preserves tool/model/dir).
|
|
1017
|
+
const solveResumeCmd = __codexBuildSolveResumeCmd(argv, sessionId, tempDir);
|
|
1014
1018
|
const messageLines = formatUsageLimitMessage({
|
|
1015
1019
|
tool: 'OpenAI Codex',
|
|
1016
1020
|
resetTime: limitInfo.resetTime,
|
|
1017
1021
|
sessionId,
|
|
1018
|
-
|
|
1022
|
+
solveResumeCommand: solveResumeCmd,
|
|
1019
1023
|
});
|
|
1020
1024
|
|
|
1021
1025
|
for (const line of messageLines) {
|
|
@@ -1096,11 +1100,13 @@ export const executeCodexCommand = async params => {
|
|
|
1096
1100
|
limitResetTime = limitInfo.resetTime;
|
|
1097
1101
|
|
|
1098
1102
|
// Format and display user-friendly message
|
|
1103
|
+
// Issue #942: build proper solve resume command (preserves tool/model/dir).
|
|
1104
|
+
const solveResumeCmd = __codexBuildSolveResumeCmd(argv, sessionId, tempDir);
|
|
1099
1105
|
const messageLines = formatUsageLimitMessage({
|
|
1100
1106
|
tool: 'OpenAI Codex',
|
|
1101
1107
|
resetTime: limitInfo.resetTime,
|
|
1102
1108
|
sessionId,
|
|
1103
|
-
|
|
1109
|
+
solveResumeCommand: solveResumeCmd,
|
|
1104
1110
|
});
|
|
1105
1111
|
|
|
1106
1112
|
for (const line of messageLines) {
|
package/src/gemini.lib.mjs
CHANGED
|
@@ -13,6 +13,8 @@ import { log } from './lib.mjs';
|
|
|
13
13
|
import { reportError } from './sentry.lib.mjs';
|
|
14
14
|
import { timeouts, retryLimits } from './config.lib.mjs';
|
|
15
15
|
import { detectUsageLimit, formatUsageLimitMessage } from './usage-limit.lib.mjs';
|
|
16
|
+
import { buildSolveResumeCommand } from './solve.resume-command.lib.mjs'; // Issue #942
|
|
17
|
+
const __geminiBuildSolveResumeCmd = (argv, sessionId, tempDir) => (sessionId && argv?.url ? buildSolveResumeCommand({ issueUrl: argv.url, sessionId, tool: 'gemini', model: argv.model, fallbackModel: argv.fallbackModel, tempDir }) : null);
|
|
16
18
|
import { sanitizeObjectStrings } from './unicode-sanitization.lib.mjs';
|
|
17
19
|
import { defaultModels, geminiModels } from './models/index.mjs';
|
|
18
20
|
import { checkPlaywrightMcpPackageAvailability } from './playwright-mcp.lib.mjs';
|
|
@@ -539,11 +541,13 @@ export const executeGeminiCommand = async params => {
|
|
|
539
541
|
limitReached = true;
|
|
540
542
|
limitResetTime = limitInfo.resetTime;
|
|
541
543
|
|
|
544
|
+
// Issue #942: build proper solve resume command (preserves tool/model/dir).
|
|
545
|
+
const solveResumeCmd = __geminiBuildSolveResumeCmd(argv, sessionId, tempDir);
|
|
542
546
|
const messageLines = formatUsageLimitMessage({
|
|
543
547
|
tool: 'Gemini CLI',
|
|
544
548
|
resetTime: limitInfo.resetTime,
|
|
545
549
|
sessionId,
|
|
546
|
-
|
|
550
|
+
solveResumeCommand: solveResumeCmd,
|
|
547
551
|
});
|
|
548
552
|
|
|
549
553
|
for (const line of messageLines) {
|
package/src/solve.mjs
CHANGED
|
@@ -31,7 +31,7 @@ const { processAutoContinueForIssue } = autoContinue;
|
|
|
31
31
|
const repository = await import('./solve.repository.lib.mjs');
|
|
32
32
|
const { setupTempDirectory, cleanupTempDirectory } = repository;
|
|
33
33
|
const results = await import('./solve.results.lib.mjs');
|
|
34
|
-
const { cleanupClaudeFile, showSessionSummary, verifyResults, buildClaudeResumeCommand, buildSolveResumeCommand, maybeAttachWorkingSessionSummary, verifyPullRequestIssueLinkAfterAutoRestart } = results;
|
|
34
|
+
const { cleanupClaudeFile, showSessionSummary, verifyResults, buildClaudeResumeCommand, buildClaudeAutonomousResumeCommand, buildSolveResumeCommand, maybeAttachWorkingSessionSummary, verifyPullRequestIssueLinkAfterAutoRestart } = results;
|
|
35
35
|
const claudeLib = await import('./claude.lib.mjs');
|
|
36
36
|
const { executeClaude, checkPlaywrightMcpAvailability } = claudeLib;
|
|
37
37
|
const githubLinking = await import('./github-linking.lib.mjs');
|
|
@@ -868,16 +868,14 @@ try {
|
|
|
868
868
|
await log(`ā° Limit resets at: ${formattedResetTime}`);
|
|
869
869
|
}
|
|
870
870
|
await log('');
|
|
871
|
-
// Show
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
await log('š” To continue this session in Claude Code interactive mode:');
|
|
877
|
-
await log('');
|
|
878
|
-
await log(` ${claudeResumeCmd}`);
|
|
871
|
+
// Show dual resume commands (interactive + autonomous) only for --tool claude
|
|
872
|
+
if ((argv.tool || 'claude') === 'claude') {
|
|
873
|
+
await log('š” To continue this session:');
|
|
874
|
+
await log(` Interactive mode: ${buildClaudeResumeCommand({ tempDir, sessionId, model: argv.model })}`);
|
|
875
|
+
await log(` Autonomous mode: ${buildClaudeAutonomousResumeCommand({ tempDir, sessionId, model: argv.model })}`);
|
|
879
876
|
await log('');
|
|
880
877
|
} else if (argv.url) {
|
|
878
|
+
const toolForResume = argv.tool || 'claude';
|
|
881
879
|
const solveResumeCmd = buildSolveResumeCommand({ issueUrl: argv.url, sessionId, tool: toolForResume, model: argv.model, fallbackModel: argv.fallbackModel, tempDir });
|
|
882
880
|
await log(`š” To continue this ${toolForResume} session with solve:`);
|
|
883
881
|
await log('');
|
|
@@ -926,13 +924,13 @@ try {
|
|
|
926
924
|
await log(` ā ļø Error uploading logs: ${uploadError.message}`);
|
|
927
925
|
}
|
|
928
926
|
} else if (prNumber) {
|
|
929
|
-
// Fallback: Post simple failure comment
|
|
927
|
+
// Fallback: Post simple failure comment (no CLI commands in GitHub comments, only mention option)
|
|
930
928
|
try {
|
|
931
929
|
const resetTime = global.limitResetTime;
|
|
932
|
-
//
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
const resumeSection =
|
|
930
|
+
// Issue #942: do not embed CLI commands in GitHub comments. Users
|
|
931
|
+
// interact via the Telegram bot, not the CLI. The full resume
|
|
932
|
+
// commands (interactive/autonomous/solve) live in the attached logs.
|
|
933
|
+
const resumeSection = sessionId ? `Session ID: \`${sessionId}\`\n\nUse the \`--auto-resume-on-limit-reset\` or \`--auto-restart-on-limit-reset\` option to automatically resume when the limit resets.` : 'Use the `--auto-resume-on-limit-reset` or `--auto-restart-on-limit-reset` option to automatically resume when the limit resets.';
|
|
936
934
|
// Format the reset time with relative time and UTC conversion if available
|
|
937
935
|
const timezone = global.limitTimezone || null;
|
|
938
936
|
const formattedResetTime = resetTime ? formatResetTimeWithRelative(resetTime, timezone) : null;
|
|
@@ -1039,21 +1037,22 @@ try {
|
|
|
1039
1037
|
// Skip failure exit if limit reached with auto-resume (continues to showSessionSummary/autoContinueWhenLimitResets)
|
|
1040
1038
|
const shouldSkipFailureExitForAutoLimitContinue = limitReached && argv.autoResumeOnLimitReset;
|
|
1041
1039
|
if (!success && !shouldSkipFailureExitForAutoLimitContinue) {
|
|
1042
|
-
//
|
|
1040
|
+
// Issue #942: show all three resume options on failure for richer guidance.
|
|
1041
|
+
// 1. Interactive claude - opens Claude Code interactively (claude only)
|
|
1042
|
+
// 2. Autonomous claude - one-shot claude --resume w/ --dangerously-skip-permissions -p (claude only)
|
|
1043
|
+
// 3. Solve resume - re-enters solve.mjs with --resume, preserving tool/model/dir
|
|
1043
1044
|
const toolForFailure = argv.tool || 'claude';
|
|
1044
|
-
if (sessionId
|
|
1045
|
-
const claudeResumeCmd = buildClaudeResumeCommand({ tempDir, sessionId, model: argv.model });
|
|
1046
|
-
await log('');
|
|
1047
|
-
await log('š” To continue this session in Claude Code interactive mode:');
|
|
1048
|
-
await log('');
|
|
1049
|
-
await log(` ${claudeResumeCmd}`);
|
|
1045
|
+
if (sessionId) {
|
|
1050
1046
|
await log('');
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1047
|
+
await log('š” To continue this session:');
|
|
1048
|
+
if (toolForFailure === 'claude') {
|
|
1049
|
+
await log(` Interactive mode: ${buildClaudeResumeCommand({ tempDir, sessionId, model: argv.model })}`);
|
|
1050
|
+
await log(` Autonomous mode: ${buildClaudeAutonomousResumeCommand({ tempDir, sessionId, model: argv.model })}`);
|
|
1051
|
+
}
|
|
1052
|
+
if (argv.url) {
|
|
1053
|
+
const solveResumeCmd = buildSolveResumeCommand({ issueUrl: argv.url, sessionId, tool: toolForFailure, model: argv.model, fallbackModel: argv.fallbackModel, tempDir });
|
|
1054
|
+
await log(` Solve resume mode: ${solveResumeCmd}`);
|
|
1055
|
+
}
|
|
1057
1056
|
await log('');
|
|
1058
1057
|
}
|
|
1059
1058
|
|
|
@@ -43,48 +43,16 @@ const { autoContinueWhenLimitResets } = autoContinue;
|
|
|
43
43
|
// Import Claude-specific command builders
|
|
44
44
|
// These are used to generate copy-pasteable Claude CLI resume commands for users
|
|
45
45
|
// Pattern: (cd "/tmp/gh-issue-solver-..." && claude --resume <session-id>)
|
|
46
|
+
// Two types of resume commands are supported:
|
|
47
|
+
// 1. Interactive resume: Short command that opens interactive mode
|
|
48
|
+
// 2. Autonomous resume: Full command with all flags to run autonomously
|
|
46
49
|
const claudeCommandBuilder = await import('./claude.command-builder.lib.mjs');
|
|
47
|
-
export const { buildClaudeResumeCommand, buildClaudeInitialCommand } = claudeCommandBuilder;
|
|
50
|
+
export const { buildClaudeResumeCommand, buildClaudeAutonomousResumeCommand, buildClaudeInitialCommand } = claudeCommandBuilder;
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
*
|
|
54
|
-
* @param {Object} options
|
|
55
|
-
* @param {string} options.issueUrl - The issue URL passed to solve.mjs
|
|
56
|
-
* @param {string} options.sessionId - The session ID to resume
|
|
57
|
-
* @param {string|null} [options.tool] - Tool name (codex, opencode, agent, gemini)
|
|
58
|
-
* @param {string|null} [options.model] - Model name to preserve
|
|
59
|
-
* @param {string|null} [options.fallbackModel] - Explicit fallback model to preserve
|
|
60
|
-
* @param {string|null} [options.tempDir] - Working directory to preserve
|
|
61
|
-
* @param {string} [options.nodePath] - Node binary path
|
|
62
|
-
* @param {string} [options.scriptPath] - solve.mjs path
|
|
63
|
-
* @returns {string}
|
|
64
|
-
*/
|
|
65
|
-
export const buildSolveResumeCommand = ({ issueUrl, sessionId, tool = null, model = null, fallbackModel = null, tempDir = null, nodePath = process.argv[0], scriptPath = process.argv[1] }) => {
|
|
66
|
-
const shellQuote = value => `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
67
|
-
|
|
68
|
-
const args = [shellQuote(scriptPath), shellQuote(issueUrl), '--resume', shellQuote(sessionId)];
|
|
69
|
-
|
|
70
|
-
if (tool && tool !== 'claude') {
|
|
71
|
-
args.push('--tool', shellQuote(tool));
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (model) {
|
|
75
|
-
args.push('--model', shellQuote(model));
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (fallbackModel) {
|
|
79
|
-
args.push('--fallback-model', shellQuote(fallbackModel));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (tempDir) {
|
|
83
|
-
args.push('--working-directory', shellQuote(tempDir));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return `${shellQuote(nodePath)} ${args.join(' ')}`;
|
|
87
|
-
};
|
|
52
|
+
// Issue #942: buildSolveResumeCommand lives in its own module so it can be safely
|
|
53
|
+
// imported from tool libraries (claude/codex/gemini) without circular imports.
|
|
54
|
+
import { buildSolveResumeCommand } from './solve.resume-command.lib.mjs';
|
|
55
|
+
export { buildSolveResumeCommand };
|
|
88
56
|
|
|
89
57
|
// Import error handling functions
|
|
90
58
|
// const errorHandlers = await import('./solve.error-handlers.lib.mjs'); // Not currently used
|
|
@@ -605,23 +573,23 @@ export const showSessionSummary = async (sessionId, limitReached, argv, issueUrl
|
|
|
605
573
|
const absoluteLogPath = path.resolve(getLogFile());
|
|
606
574
|
await log(`ā
Complete log file: ${absoluteLogPath}`);
|
|
607
575
|
|
|
576
|
+
// Show three resume options:
|
|
577
|
+
// 1. Interactive claude - opens Claude Code interactively (claude only)
|
|
578
|
+
// 2. Autonomous claude - one-shot claude --resume w/ --dangerously-skip-permissions -p (claude only)
|
|
579
|
+
// 3. Solve resume - re-enters solve.mjs with --resume so the full flow continues
|
|
580
|
+
// (works for every supported tool, including codex/gemini/etc.)
|
|
608
581
|
const tool = argv.tool || 'claude';
|
|
582
|
+
await log('');
|
|
583
|
+
await log('š” To continue this session:');
|
|
609
584
|
if (tool === 'claude') {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
await log('');
|
|
615
|
-
await log(` ${claudeResumeCmd}`);
|
|
616
|
-
await log('');
|
|
617
|
-
} else if (issueUrl) {
|
|
585
|
+
await log(` Interactive mode: ${buildClaudeResumeCommand({ tempDir, sessionId, model: argv.model })}`);
|
|
586
|
+
await log(` Autonomous mode: ${buildClaudeAutonomousResumeCommand({ tempDir, sessionId, model: argv.model })}`);
|
|
587
|
+
}
|
|
588
|
+
if (issueUrl) {
|
|
618
589
|
const solveResumeCmd = buildSolveResumeCommand({ issueUrl, sessionId, tool, model: argv.model, fallbackModel: argv.fallbackModel, tempDir });
|
|
619
|
-
await log(
|
|
620
|
-
await log(`š” To continue this ${tool} session with solve:`);
|
|
621
|
-
await log('');
|
|
622
|
-
await log(` ${solveResumeCmd}`);
|
|
623
|
-
await log('');
|
|
590
|
+
await log(` Solve resume mode: ${solveResumeCmd}`);
|
|
624
591
|
}
|
|
592
|
+
await log('');
|
|
625
593
|
|
|
626
594
|
if (limitReached) {
|
|
627
595
|
await log('ā° LIMIT REACHED DETECTED!');
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Build a solve.mjs resume command for tools that do not have a first-party interactive
|
|
5
|
+
* resume CLI flow like Claude Code. This keeps the invocation within hive-mind so the
|
|
6
|
+
* original tool selection and working directory can be preserved.
|
|
7
|
+
*
|
|
8
|
+
* Lives in its own module (not solve.results.lib.mjs) so it can be imported from
|
|
9
|
+
* claude.lib.mjs / codex.lib.mjs / gemini.lib.mjs without creating a circular import.
|
|
10
|
+
* See issue #942.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} options
|
|
13
|
+
* @param {string} options.issueUrl - The issue URL passed to solve.mjs
|
|
14
|
+
* @param {string} options.sessionId - The session ID to resume
|
|
15
|
+
* @param {string|null} [options.tool] - Tool name (codex, opencode, agent, gemini)
|
|
16
|
+
* @param {string|null} [options.model] - Model name to preserve
|
|
17
|
+
* @param {string|null} [options.fallbackModel] - Explicit fallback model to preserve
|
|
18
|
+
* @param {string|null} [options.tempDir] - Working directory to preserve
|
|
19
|
+
* @param {string} [options.nodePath] - Node binary path
|
|
20
|
+
* @param {string} [options.scriptPath] - solve.mjs path
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
export const buildSolveResumeCommand = ({ issueUrl, sessionId, tool = null, model = null, fallbackModel = null, tempDir = null, nodePath = process.argv[0], scriptPath = process.argv[1] }) => {
|
|
24
|
+
const shellQuote = value => `"${String(value).replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
25
|
+
const args = [shellQuote(scriptPath), shellQuote(issueUrl), '--resume', shellQuote(sessionId)];
|
|
26
|
+
if (tool && tool !== 'claude') args.push('--tool', shellQuote(tool));
|
|
27
|
+
if (model) args.push('--model', shellQuote(model));
|
|
28
|
+
if (fallbackModel) args.push('--fallback-model', shellQuote(fallbackModel));
|
|
29
|
+
if (tempDir) args.push('--working-directory', shellQuote(tempDir));
|
|
30
|
+
return `${shellQuote(nodePath)} ${args.join(' ')}`;
|
|
31
|
+
};
|
package/src/usage-limit.lib.mjs
CHANGED
|
@@ -394,10 +394,14 @@ export function formatResetTimeWithRelative(resetTime, timezone = null) {
|
|
|
394
394
|
* @param {string} options.tool - Tool name (claude, codex, opencode, agent, gemini)
|
|
395
395
|
* @param {string|null} options.resetTime - Time when limit resets
|
|
396
396
|
* @param {string|null} options.sessionId - Session ID for resuming
|
|
397
|
-
* @param {string|null} options.resumeCommand -
|
|
397
|
+
* @param {string|null} options.resumeCommand - Interactive resume command (legacy, for backward compatibility)
|
|
398
|
+
* @param {string|null} options.interactiveResumeCommand - Command to resume in interactive mode (claude)
|
|
399
|
+
* @param {string|null} options.autonomousResumeCommand - Command to resume in autonomous mode (claude)
|
|
400
|
+
* @param {string|null} options.solveResumeCommand - Command to resume the full solve.mjs flow
|
|
401
|
+
* (works for every supported tool, including codex/gemini/etc.)
|
|
398
402
|
* @returns {string[]} - Array of formatted message lines
|
|
399
403
|
*/
|
|
400
|
-
export function formatUsageLimitMessage({ tool, resetTime, sessionId, resumeCommand }) {
|
|
404
|
+
export function formatUsageLimitMessage({ tool, resetTime, sessionId, resumeCommand, interactiveResumeCommand, autonomousResumeCommand, solveResumeCommand }) {
|
|
401
405
|
const lines = ['', 'ā³ Usage Limit Reached!', '', `Your ${tool || 'AI tool'} usage limit has been reached.`];
|
|
402
406
|
|
|
403
407
|
if (resetTime) {
|
|
@@ -406,12 +410,29 @@ export function formatUsageLimitMessage({ tool, resetTime, sessionId, resumeComm
|
|
|
406
410
|
lines.push('Please wait for the limit to reset.');
|
|
407
411
|
}
|
|
408
412
|
|
|
409
|
-
|
|
413
|
+
// Support both new (dual command) and legacy (single command) formats
|
|
414
|
+
const interactiveCmd = interactiveResumeCommand || resumeCommand;
|
|
415
|
+
|
|
416
|
+
if (sessionId && (interactiveCmd || autonomousResumeCommand || solveResumeCommand)) {
|
|
410
417
|
lines.push('');
|
|
411
418
|
lines.push(`š Session ID: ${sessionId}`);
|
|
412
419
|
lines.push('');
|
|
413
|
-
lines.push('To resume this session after the limit resets
|
|
414
|
-
|
|
420
|
+
lines.push('To resume this session after the limit resets:');
|
|
421
|
+
if (interactiveCmd) {
|
|
422
|
+
lines.push('');
|
|
423
|
+
lines.push(' Interactive mode (opens Claude Code for user interaction):');
|
|
424
|
+
lines.push(` ${interactiveCmd}`);
|
|
425
|
+
}
|
|
426
|
+
if (autonomousResumeCommand) {
|
|
427
|
+
lines.push('');
|
|
428
|
+
lines.push(' Autonomous mode (continues work without user interaction):');
|
|
429
|
+
lines.push(` ${autonomousResumeCommand}`);
|
|
430
|
+
}
|
|
431
|
+
if (solveResumeCommand) {
|
|
432
|
+
lines.push('');
|
|
433
|
+
lines.push(' Solve resume mode (re-enters the full solve flow, preserves tool/model/dir):');
|
|
434
|
+
lines.push(` ${solveResumeCommand}`);
|
|
435
|
+
}
|
|
415
436
|
}
|
|
416
437
|
|
|
417
438
|
lines.push('');
|