@link-assistant/hive-mind 1.71.1 → 1.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/CLAUDE.md +3 -3
- package/package.json +1 -1
- package/src/claude.prompts.lib.mjs +9 -0
- package/src/solve.config.lib.mjs +5 -0
- package/src/solve.minimal-restart-prompt.lib.mjs +90 -0
- package/src/solve.mjs +7 -0
- package/src/solve.watch.lib.mjs +38 -2
package/CHANGELOG.md
CHANGED
package/CLAUDE.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
Issue to solve:
|
|
2
|
-
Your prepared branch: issue-
|
|
3
|
-
Your prepared working directory: /tmp/gh-issue-solver-
|
|
1
|
+
Issue to solve: undefined
|
|
2
|
+
Your prepared branch: issue-644-cf3b8243
|
|
3
|
+
Your prepared working directory: /tmp/gh-issue-solver-1762010397765
|
|
4
4
|
|
|
5
5
|
Proceed.
|
package/package.json
CHANGED
|
@@ -17,6 +17,11 @@ import { buildWorkLanguageDirective } from './work-language.prompts.lib.mjs';
|
|
|
17
17
|
export const buildUserPrompt = params => {
|
|
18
18
|
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines, claudeVersion } = params;
|
|
19
19
|
|
|
20
|
+
if (argv?.minimalRestartContext && argv.resume) {
|
|
21
|
+
const lines = feedbackLines && feedbackLines.length > 0 ? feedbackLines : ['Continue the auto-restart from the previous resumed session.'];
|
|
22
|
+
return `${lines.join('\n')}\n`;
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
const promptLines = [];
|
|
21
26
|
|
|
22
27
|
// Issue or PR reference
|
|
@@ -87,6 +92,10 @@ export const buildUserPrompt = params => {
|
|
|
87
92
|
export const buildSystemPrompt = params => {
|
|
88
93
|
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision, forkedRepo } = params;
|
|
89
94
|
|
|
95
|
+
if (argv?.minimalRestartContext && argv.resume) {
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
|
|
90
99
|
// When in fork mode, screenshots are pushed to the fork, not the original repo
|
|
91
100
|
const screenshotRepoPath = argv?.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
|
|
92
101
|
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -187,6 +187,11 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
187
187
|
description: 'Maximum number of auto-restart iterations before stopping (default: 5, 0 = unlimited)',
|
|
188
188
|
default: 5,
|
|
189
189
|
},
|
|
190
|
+
'resume-on-auto-restart': {
|
|
191
|
+
type: 'boolean',
|
|
192
|
+
description: '[EXPERIMENTAL] Resume the previous Claude session on uncommitted-change auto-restart and send only a minimal restart prompt. Disabled by default.',
|
|
193
|
+
default: false,
|
|
194
|
+
},
|
|
190
195
|
'auto-resume-max-iterations': {
|
|
191
196
|
type: 'number',
|
|
192
197
|
description: 'Maximum number of automatic resume/restart continuations after usage-limit resets (default: 5, 0 = unlimited)',
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate minimal prompt for auto-restart with session resume
|
|
5
|
+
* This module provides functions to create lightweight prompts for auto-restart
|
|
6
|
+
* that assume the AI has full context from the previous session
|
|
7
|
+
*
|
|
8
|
+
* Part of the cost optimization feature for issue #661
|
|
9
|
+
* @see case-studies/issue-661-session-resume-cost-optimization/
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Note: This module does not import $ directly
|
|
13
|
+
// Functions receive $ as a parameter from the calling module
|
|
14
|
+
// This ensures consistent command executor usage across the codebase
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate minimal prompt for auto-restart with session resume
|
|
18
|
+
* This prompt assumes the AI has full context from the previous session
|
|
19
|
+
* Target: ~500 tokens (compared to 50k-200k in full context)
|
|
20
|
+
*
|
|
21
|
+
* @param {string} tempDir - Working directory
|
|
22
|
+
* @param {object} $ - Command executor
|
|
23
|
+
* @returns {Promise<string>} Minimal restart prompt
|
|
24
|
+
*/
|
|
25
|
+
export const generateMinimalRestartPrompt = async (tempDir, $) => {
|
|
26
|
+
// Get uncommitted changes
|
|
27
|
+
const gitStatus = await $({ cwd: tempDir })`git status --porcelain`;
|
|
28
|
+
const uncommittedFiles = gitStatus.stdout.toString().trim();
|
|
29
|
+
|
|
30
|
+
// Get brief diff summaries (not full diffs to keep the prompt minimal)
|
|
31
|
+
const gitDiffStat = await $({ cwd: tempDir })`git diff --stat`;
|
|
32
|
+
const unstagedDiffSummary = gitDiffStat.stdout.toString().trim();
|
|
33
|
+
const gitCachedDiffStat = await $({ cwd: tempDir })`git diff --cached --stat`;
|
|
34
|
+
const stagedDiffSummary = gitCachedDiffStat.stdout.toString().trim();
|
|
35
|
+
const summarySections = [];
|
|
36
|
+
if (unstagedDiffSummary) summarySections.push(`Unstaged changes:\n${unstagedDiffSummary}`);
|
|
37
|
+
if (stagedDiffSummary) summarySections.push(`Staged changes:\n${stagedDiffSummary}`);
|
|
38
|
+
const diffSummary = summarySections.join('\n\n') || 'No tracked-file diff summary available.';
|
|
39
|
+
|
|
40
|
+
// Count changes
|
|
41
|
+
const fileCount = uncommittedFiles.split('\n').filter(line => line.trim()).length;
|
|
42
|
+
|
|
43
|
+
return `🔄 Auto-restart: resume the previous session and handle its uncommitted changes.
|
|
44
|
+
|
|
45
|
+
Uncommitted files (${fileCount}):
|
|
46
|
+
${uncommittedFiles}
|
|
47
|
+
|
|
48
|
+
Changes summary:
|
|
49
|
+
${diffSummary}
|
|
50
|
+
|
|
51
|
+
Please review these changes and commit them with an appropriate commit message.
|
|
52
|
+
Follow the repository's commit message conventions from previous commits.`;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Generate full context prompt (fallback when resume fails or not enabled)
|
|
57
|
+
* This is used when session resume is not available or failed
|
|
58
|
+
*
|
|
59
|
+
* @param {string} issueUrl - Issue URL
|
|
60
|
+
* @param {string} issueBody - Issue description
|
|
61
|
+
* @param {number} prNumber - PR number
|
|
62
|
+
* @param {Array<string>} feedbackLines - Feedback from reviewers
|
|
63
|
+
* @param {string} tempDir - Working directory
|
|
64
|
+
* @param {object} $ - Command executor
|
|
65
|
+
* @returns {Promise<string>} Full restart prompt
|
|
66
|
+
*/
|
|
67
|
+
export const generateFullRestartPrompt = async (issueUrl, issueBody, prNumber, feedbackLines, tempDir, $) => {
|
|
68
|
+
// Get uncommitted changes with full diff
|
|
69
|
+
const gitStatus = await $({ cwd: tempDir })`git status --porcelain`;
|
|
70
|
+
const uncommittedFiles = gitStatus.stdout.toString().trim();
|
|
71
|
+
|
|
72
|
+
const gitDiff = await $({ cwd: tempDir })`git diff`;
|
|
73
|
+
const fullDiff = gitDiff.stdout.toString();
|
|
74
|
+
|
|
75
|
+
let prompt = `
|
|
76
|
+
Continuing work on issue: ${issueUrl}
|
|
77
|
+
|
|
78
|
+
Previous session completed but left uncommitted changes.
|
|
79
|
+
`.trim();
|
|
80
|
+
|
|
81
|
+
if (feedbackLines && feedbackLines.length > 0) {
|
|
82
|
+
prompt += `\n\nFeedback from reviewers:\n${feedbackLines.join('\n')}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
prompt += `\n\nUncommitted changes:\n${uncommittedFiles}\n\nFull diff:\n${fullDiff}`;
|
|
86
|
+
|
|
87
|
+
prompt += '\n\nPlease review these changes and commit them appropriately.';
|
|
88
|
+
|
|
89
|
+
return prompt;
|
|
90
|
+
};
|
package/src/solve.mjs
CHANGED
|
@@ -826,6 +826,13 @@ try {
|
|
|
826
826
|
limitReached = toolResult.limitReached;
|
|
827
827
|
cleanupContext.limitReached = limitReached;
|
|
828
828
|
|
|
829
|
+
if (sessionId && (argv.resumeOnAutoRestart || argv['resume-on-auto-restart'])) {
|
|
830
|
+
global.previousSessionId = sessionId;
|
|
831
|
+
if (argv.verbose) {
|
|
832
|
+
await log(`Session ID stored for auto-restart resume: ${sessionId}`, { verbose: true });
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
|
|
829
836
|
// Capture limit reset time and timezone globally for downstream handlers (auto-continue, cleanup decisions)
|
|
830
837
|
if (toolResult && toolResult.limitResetTime) {
|
|
831
838
|
global.limitResetTime = toolResult.limitResetTime;
|
package/src/solve.watch.lib.mjs
CHANGED
|
@@ -290,6 +290,38 @@ export const watchForFeedback = async params => {
|
|
|
290
290
|
// to comments posted during *this* iteration only, not across the whole watch loop.
|
|
291
291
|
const iterationStartTime = new Date();
|
|
292
292
|
|
|
293
|
+
let restartFeedbackLines = feedbackLines;
|
|
294
|
+
let restartArgv = argv;
|
|
295
|
+
const shouldUseSessionResume = Boolean(isTemporaryWatch && (firstIterationInTemporaryMode || hasUncommittedInTempMode) && (argv.resumeOnAutoRestart || argv['resume-on-auto-restart']) && (argv.tool === 'claude' || !argv.tool) && global.previousSessionId);
|
|
296
|
+
|
|
297
|
+
if (shouldUseSessionResume) {
|
|
298
|
+
await log(formatAligned('', 'Experimental session resume: using minimal auto-restart prompt', '', 2));
|
|
299
|
+
await log(formatAligned('', `Resuming session: ${global.previousSessionId}`, '', 2));
|
|
300
|
+
|
|
301
|
+
if (argv.verbose) {
|
|
302
|
+
try {
|
|
303
|
+
const { calculateSessionTokens } = await import('./claude.lib.mjs');
|
|
304
|
+
const tokenUsage = await calculateSessionTokens(global.previousSessionId, tempDir);
|
|
305
|
+
if (tokenUsage?.totalTokens) {
|
|
306
|
+
await log(formatAligned('', `Previous session tokens: ${tokenUsage.totalTokens.toLocaleString()}`, '', 2));
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
await log(formatAligned('', 'Could not read previous session token usage', '', 2));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const { generateMinimalRestartPrompt } = await import('./solve.minimal-restart-prompt.lib.mjs');
|
|
314
|
+
const minimalPrompt = await generateMinimalRestartPrompt(tempDir, $);
|
|
315
|
+
restartFeedbackLines = [minimalPrompt];
|
|
316
|
+
restartArgv = {
|
|
317
|
+
...argv,
|
|
318
|
+
resume: global.previousSessionId,
|
|
319
|
+
minimalRestartContext: true,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
await log(formatAligned('', `Minimal restart prompt size: ${minimalPrompt.length} characters`, '', 2));
|
|
323
|
+
}
|
|
324
|
+
|
|
293
325
|
// Execute tool using shared utility
|
|
294
326
|
const toolResult = await executeToolIteration({
|
|
295
327
|
issueUrl,
|
|
@@ -300,10 +332,14 @@ export const watchForFeedback = async params => {
|
|
|
300
332
|
branchName: prBranch || branchName,
|
|
301
333
|
tempDir,
|
|
302
334
|
mergeStateStatus,
|
|
303
|
-
feedbackLines,
|
|
304
|
-
argv,
|
|
335
|
+
feedbackLines: restartFeedbackLines,
|
|
336
|
+
argv: restartArgv,
|
|
305
337
|
});
|
|
306
338
|
|
|
339
|
+
if (toolResult.sessionId && (argv.resumeOnAutoRestart || argv['resume-on-auto-restart'])) {
|
|
340
|
+
global.previousSessionId = toolResult.sessionId;
|
|
341
|
+
}
|
|
342
|
+
|
|
307
343
|
if (!toolResult.success) {
|
|
308
344
|
// Check if this is an API error using shared utility
|
|
309
345
|
if (isApiError(toolResult)) {
|