@link-assistant/hive-mind 1.1.0 ā 1.2.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 +58 -0
- package/package.json +1 -1
- package/src/hive.config.lib.mjs +5 -0
- package/src/hive.mjs +13 -0
- package/src/hive.recheck.lib.mjs +86 -0
- package/src/review.mjs +17 -9
- package/src/solve.config.lib.mjs +5 -0
- package/src/solve.mjs +1 -1
- package/src/task.mjs +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Add experimental --execute-tool-with-bun option to improve speed and memory usage
|
|
8
|
+
|
|
9
|
+
This feature adds the `--execute-tool-with-bun` option that allows users to execute the AI tool using `bunx claude` instead of `claude`, which may provide performance benefits in terms of speed and memory usage.
|
|
10
|
+
|
|
11
|
+
**Supported commands:**
|
|
12
|
+
- `solve` - Uses `bunx claude` when option is enabled
|
|
13
|
+
- `task` - Uses `bunx claude` when option is enabled
|
|
14
|
+
- `review` - Uses `bunx claude` when option is enabled
|
|
15
|
+
- `hive` - Passes the option through to the `solve` subprocess
|
|
16
|
+
|
|
17
|
+
**How It Works:**
|
|
18
|
+
When `--execute-tool-with-bun` is enabled, the `claudePath` variable is set to `'bunx claude'` instead of `'claude'` (or `CLAUDE_PATH` environment variable).
|
|
19
|
+
|
|
20
|
+
**Usage Examples:**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Use with solve command
|
|
24
|
+
solve https://github.com/owner/repo/issues/123 --execute-tool-with-bun
|
|
25
|
+
|
|
26
|
+
# Use with task command
|
|
27
|
+
task "implement feature X" --execute-tool-with-bun
|
|
28
|
+
|
|
29
|
+
# Use with review command
|
|
30
|
+
review https://github.com/owner/repo/pull/456 --execute-tool-with-bun
|
|
31
|
+
|
|
32
|
+
# Use with hive command (passes through to solve)
|
|
33
|
+
hive https://github.com/owner/repo --execute-tool-with-bun
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The option defaults to `false` to maintain backward compatibility.
|
|
37
|
+
|
|
38
|
+
Fixes #812
|
|
39
|
+
|
|
40
|
+
feat(hive): recheck issue conditions before processing queue items
|
|
41
|
+
|
|
42
|
+
Added `recheckIssueConditions()` function to validate issue state right before processing,
|
|
43
|
+
preventing wasted resources on issues that should be skipped due to changed conditions since queuing.
|
|
44
|
+
|
|
45
|
+
**Checks performed:**
|
|
46
|
+
- **Issue state**: Verifies the issue is still open
|
|
47
|
+
- **Open PRs**: Checks if issue has PRs (when `--skip-issues-with-prs` is enabled)
|
|
48
|
+
- **Repository status**: Confirms repository is not archived
|
|
49
|
+
|
|
50
|
+
**Benefits:**
|
|
51
|
+
- Prevents processing closed issues
|
|
52
|
+
- Avoids duplicate work when PRs already exist
|
|
53
|
+
- Stops work on newly archived repositories
|
|
54
|
+
- Saves AI model tokens and compute resources
|
|
55
|
+
|
|
56
|
+
**Performance impact:**
|
|
57
|
+
Minimal overhead per issue (~300-500ms for API calls), negligible compared to 5-15 minute solve time.
|
|
58
|
+
|
|
59
|
+
Fixes #810
|
|
60
|
+
|
|
3
61
|
## 1.1.0
|
|
4
62
|
|
|
5
63
|
### Minor Changes
|
package/package.json
CHANGED
package/src/hive.config.lib.mjs
CHANGED
|
@@ -286,6 +286,11 @@ export const createYargsConfig = yargsInstance => {
|
|
|
286
286
|
description: '[EXPERIMENTAL] Include guidance for managing REQUIREMENTS.md and ARCHITECTURE.md files. When enabled, agents will update these documentation files when changes affect requirements or architecture.',
|
|
287
287
|
default: false,
|
|
288
288
|
})
|
|
289
|
+
.option('execute-tool-with-bun', {
|
|
290
|
+
type: 'boolean',
|
|
291
|
+
description: 'Execute the AI tool using bunx (experimental, may improve speed and memory usage) - passed to solve command',
|
|
292
|
+
default: false,
|
|
293
|
+
})
|
|
289
294
|
.parserConfiguration({
|
|
290
295
|
'boolean-negation': true,
|
|
291
296
|
'strip-dashed': false,
|
package/src/hive.mjs
CHANGED
|
@@ -110,6 +110,8 @@ if (isDirectExecution) {
|
|
|
110
110
|
const { tryFetchIssuesWithGraphQL } = graphqlLib;
|
|
111
111
|
const solutionDraftsLib = await import('./list-solution-drafts.lib.mjs');
|
|
112
112
|
const { listSolutionDrafts } = solutionDraftsLib;
|
|
113
|
+
const recheckLib = await import('./hive.recheck.lib.mjs');
|
|
114
|
+
const { recheckIssueConditions } = recheckLib;
|
|
113
115
|
const commandName = process.argv[1] ? process.argv[1].split('/').pop() : '';
|
|
114
116
|
const isLocalScript = commandName.endsWith('.mjs');
|
|
115
117
|
const solveCommand = isLocalScript ? './solve.mjs' : 'solve';
|
|
@@ -713,6 +715,16 @@ if (isDirectExecution) {
|
|
|
713
715
|
|
|
714
716
|
await log(`\nš· Worker ${workerId} processing: ${issueUrl}`);
|
|
715
717
|
|
|
718
|
+
// Recheck conditions before processing to avoid wasted work
|
|
719
|
+
const recheckResult = await recheckIssueConditions(issueUrl, argv);
|
|
720
|
+
if (!recheckResult.shouldProcess) {
|
|
721
|
+
await log(` āļø Skipping issue: ${recheckResult.reason}`);
|
|
722
|
+
issueQueue.markCompleted(issueUrl);
|
|
723
|
+
const stats = issueQueue.getStats();
|
|
724
|
+
await log(` š Queue: ${stats.queued} waiting, ${stats.processing} processing, ${stats.completed} completed, ${stats.failed} failed`);
|
|
725
|
+
continue;
|
|
726
|
+
}
|
|
727
|
+
|
|
716
728
|
// Track if this issue failed
|
|
717
729
|
let issueFailed = false;
|
|
718
730
|
|
|
@@ -756,6 +768,7 @@ if (isDirectExecution) {
|
|
|
756
768
|
if (argv.promptIssueReporting) args.push('--prompt-issue-reporting');
|
|
757
769
|
if (argv.promptCaseStudies) args.push('--prompt-case-studies');
|
|
758
770
|
if (argv.promptPlaywrightMcp !== undefined) args.push(argv.promptPlaywrightMcp ? '--prompt-playwright-mcp' : '--no-prompt-playwright-mcp');
|
|
771
|
+
if (argv.executeToolWithBun) args.push('--execute-tool-with-bun');
|
|
759
772
|
// Log the actual command being executed so users can investigate/reproduce
|
|
760
773
|
await log(` š Command: ${solveCommand} ${args.join(' ')}`);
|
|
761
774
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Library for rechecking issue conditions in hive queue processing
|
|
3
|
+
|
|
4
|
+
import { log, cleanErrorMessage } from './lib.mjs';
|
|
5
|
+
import { batchCheckPullRequestsForIssues, batchCheckArchivedRepositories } from './github.lib.mjs';
|
|
6
|
+
import { reportError } from './sentry.lib.mjs';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recheck conditions for an issue right before processing
|
|
10
|
+
* This ensures the issue should still be processed even if conditions changed since queuing
|
|
11
|
+
* @param {string} issueUrl - The URL of the issue to check
|
|
12
|
+
* @param {Object} argv - Command line arguments with configuration
|
|
13
|
+
* @returns {Promise<{shouldProcess: boolean, reason?: string}>}
|
|
14
|
+
*/
|
|
15
|
+
export async function recheckIssueConditions(issueUrl, argv) {
|
|
16
|
+
try {
|
|
17
|
+
// Extract owner, repo, and issue number from URL
|
|
18
|
+
const urlMatch = issueUrl.match(/github\.com\/([^/]+)\/([^/]+)\/issues\/(\d+)/);
|
|
19
|
+
if (!urlMatch) {
|
|
20
|
+
await log(` ā ļø Could not parse issue URL: ${issueUrl}`, { verbose: true });
|
|
21
|
+
return { shouldProcess: true }; // Process anyway if we can't parse
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const [, owner, repo, issueNumber] = urlMatch;
|
|
25
|
+
const issueNum = parseInt(issueNumber);
|
|
26
|
+
|
|
27
|
+
await log(` š Rechecking conditions for issue #${issueNum}...`, { verbose: true });
|
|
28
|
+
|
|
29
|
+
// Check 1: Verify issue is still open
|
|
30
|
+
try {
|
|
31
|
+
const { execSync } = await import('child_process');
|
|
32
|
+
const issueState = execSync(`gh api repos/${owner}/${repo}/issues/${issueNum} --jq .state`, {
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
}).trim();
|
|
35
|
+
|
|
36
|
+
if (issueState === 'closed') {
|
|
37
|
+
return {
|
|
38
|
+
shouldProcess: false,
|
|
39
|
+
reason: 'Issue is now closed',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
await log(` ā
Issue is still open`, { verbose: true });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
await log(` ā ļø Could not check issue state: ${cleanErrorMessage(error)}`, { verbose: true });
|
|
45
|
+
// Continue checking other conditions
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check 2: If skipIssuesWithPrs is enabled, verify issue still has no open PRs
|
|
49
|
+
if (argv.skipIssuesWithPrs) {
|
|
50
|
+
const prResults = await batchCheckPullRequestsForIssues(owner, repo, [issueNum]);
|
|
51
|
+
const prInfo = prResults[issueNum];
|
|
52
|
+
|
|
53
|
+
if (prInfo && prInfo.openPRCount > 0) {
|
|
54
|
+
return {
|
|
55
|
+
shouldProcess: false,
|
|
56
|
+
reason: `Issue now has ${prInfo.openPRCount} open PR${prInfo.openPRCount > 1 ? 's' : ''}`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
await log(` ā
Issue still has no open PRs`, { verbose: true });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check 3: Verify repository is not archived
|
|
63
|
+
const archivedStatusMap = await batchCheckArchivedRepositories([{ owner, name: repo }]);
|
|
64
|
+
const repoKey = `${owner}/${repo}`;
|
|
65
|
+
|
|
66
|
+
if (archivedStatusMap[repoKey] === true) {
|
|
67
|
+
return {
|
|
68
|
+
shouldProcess: false,
|
|
69
|
+
reason: 'Repository is now archived',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
await log(` ā
Repository is not archived`, { verbose: true });
|
|
73
|
+
|
|
74
|
+
await log(` ā
All conditions passed, proceeding with processing`, { verbose: true });
|
|
75
|
+
return { shouldProcess: true };
|
|
76
|
+
} catch (error) {
|
|
77
|
+
reportError(error, {
|
|
78
|
+
context: 'recheck_issue_conditions',
|
|
79
|
+
issueUrl,
|
|
80
|
+
operation: 'recheck_conditions',
|
|
81
|
+
});
|
|
82
|
+
await log(` ā ļø Error rechecking conditions: ${cleanErrorMessage(error)}`, { level: 'warning' });
|
|
83
|
+
// On error, allow processing to continue (fail open)
|
|
84
|
+
return { shouldProcess: true };
|
|
85
|
+
}
|
|
86
|
+
}
|
package/src/review.mjs
CHANGED
|
@@ -19,14 +19,15 @@ if (earlyArgs.includes('--help') || earlyArgs.includes('-h')) {
|
|
|
19
19
|
// Show help and exit
|
|
20
20
|
console.log('Usage: review.mjs <pr-url> [options]');
|
|
21
21
|
console.log('\nOptions:');
|
|
22
|
-
console.log(' --version
|
|
23
|
-
console.log(' --help, -h
|
|
24
|
-
console.log(' --resume, -r
|
|
25
|
-
console.log(' --dry-run, -n
|
|
26
|
-
console.log(' --model, -m
|
|
27
|
-
console.log(' --focus, -f
|
|
28
|
-
console.log(' --approve
|
|
29
|
-
console.log(' --verbose, -v
|
|
22
|
+
console.log(' --version Show version number');
|
|
23
|
+
console.log(' --help, -h Show help');
|
|
24
|
+
console.log(' --resume, -r Resume from a previous session ID');
|
|
25
|
+
console.log(' --dry-run, -n Prepare everything but do not execute Claude');
|
|
26
|
+
console.log(' --model, -m Model to use (opus, sonnet, or full model ID) [default: opus]');
|
|
27
|
+
console.log(' --focus, -f Focus areas for review [default: all]');
|
|
28
|
+
console.log(' --approve If review passes, approve the PR');
|
|
29
|
+
console.log(' --verbose, -v Enable verbose logging');
|
|
30
|
+
console.log(' --execute-tool-with-bun Execute the AI tool using bunx (experimental) [default: false]');
|
|
30
31
|
process.exit(0);
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -91,6 +92,11 @@ const argv = yargs()
|
|
|
91
92
|
alias: 'v',
|
|
92
93
|
default: false,
|
|
93
94
|
})
|
|
95
|
+
.option('execute-tool-with-bun', {
|
|
96
|
+
type: 'boolean',
|
|
97
|
+
description: 'Execute the AI tool using bunx (experimental, may improve speed and memory usage)',
|
|
98
|
+
default: false,
|
|
99
|
+
})
|
|
94
100
|
.demandCommand(1, 'The GitHub pull request URL is required')
|
|
95
101
|
.parserConfiguration({
|
|
96
102
|
'boolean-negation': true,
|
|
@@ -126,7 +132,9 @@ if (!prUrl.match(/^https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+$/)) {
|
|
|
126
132
|
process.exit(1);
|
|
127
133
|
}
|
|
128
134
|
|
|
129
|
-
|
|
135
|
+
// Determine claude command path based on --execute-tool-with-bun option
|
|
136
|
+
// When enabled, uses 'bunx claude' which may improve speed and memory usage
|
|
137
|
+
const claudePath = argv.executeToolWithBun ? 'bunx claude' : process.env.CLAUDE_PATH || 'claude';
|
|
130
138
|
|
|
131
139
|
// Extract repository and PR number from URL
|
|
132
140
|
const urlParts = prUrl.split('/');
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -253,6 +253,11 @@ export const createYargsConfig = yargsInstance => {
|
|
|
253
253
|
choices: ['claude', 'opencode', 'codex', 'agent'],
|
|
254
254
|
default: 'claude',
|
|
255
255
|
})
|
|
256
|
+
.option('execute-tool-with-bun', {
|
|
257
|
+
type: 'boolean',
|
|
258
|
+
description: 'Execute the AI tool using bunx (experimental, may improve speed and memory usage)',
|
|
259
|
+
default: false,
|
|
260
|
+
})
|
|
256
261
|
.option('enable-workspaces', {
|
|
257
262
|
type: 'boolean',
|
|
258
263
|
description: 'Use separate workspace directory structure with repository/ and tmp/ folders. Works with all tools (claude, opencode, codex, agent). Experimental feature.',
|
package/src/solve.mjs
CHANGED
|
@@ -236,7 +236,7 @@ if (argv.verbose) {
|
|
|
236
236
|
await log(` Is Issue URL: ${!!isIssueUrl}`, { verbose: true });
|
|
237
237
|
await log(` Is PR URL: ${!!isPrUrl}`, { verbose: true });
|
|
238
238
|
}
|
|
239
|
-
const claudePath = process.env.CLAUDE_PATH || 'claude';
|
|
239
|
+
const claudePath = argv.executeToolWithBun ? 'bunx claude' : process.env.CLAUDE_PATH || 'claude';
|
|
240
240
|
// Note: owner, repo, and urlNumber are already extracted from validateGitHubUrl() above
|
|
241
241
|
// The parseUrlComponents() call was removed as it had a bug with hash fragments (#issuecomment-xyz)
|
|
242
242
|
// and the validation result already provides these values correctly parsed
|
package/src/task.mjs
CHANGED
|
@@ -124,6 +124,11 @@ const argv = yargs()
|
|
|
124
124
|
default: 'text',
|
|
125
125
|
choices: ['text', 'json'],
|
|
126
126
|
})
|
|
127
|
+
.option('execute-tool-with-bun', {
|
|
128
|
+
type: 'boolean',
|
|
129
|
+
description: 'Execute the AI tool using bunx (experimental, may improve speed and memory usage)',
|
|
130
|
+
default: false,
|
|
131
|
+
})
|
|
127
132
|
.check(argv => {
|
|
128
133
|
if (!argv['task-description'] && !argv._[0]) {
|
|
129
134
|
throw new Error('Please provide a task description');
|
|
@@ -186,7 +191,7 @@ await log(formatAligned('š”', 'Clarify mode:', argv.clarify ? 'enabled' : 'dis
|
|
|
186
191
|
await log(formatAligned('š', 'Decompose mode:', argv.decompose ? 'enabled' : 'disabled'));
|
|
187
192
|
await log(formatAligned('š', 'Output format:', argv.outputFormat));
|
|
188
193
|
|
|
189
|
-
const claudePath = process.env.CLAUDE_PATH || 'claude';
|
|
194
|
+
const claudePath = argv.executeToolWithBun ? 'bunx claude' : process.env.CLAUDE_PATH || 'claude';
|
|
190
195
|
|
|
191
196
|
// Helper function to execute Claude command
|
|
192
197
|
const executeClaude = (prompt, model) => {
|