@link-assistant/hive-mind 1.0.5 → 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 CHANGED
@@ -1,5 +1,83 @@
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
+
61
+ ## 1.1.0
62
+
63
+ ### Minor Changes
64
+
65
+ - 4c46685: Add --enable-workspaces option for separate workspace directories
66
+
67
+ This feature adds support for creating separate workspace directories for all AI tools (claude, opencode, codex, agent). When enabled with `--enable-workspaces`, the tool creates a structured workspace:
68
+ - `/tmp/hive-mind-solve-gh-{owner}/{repo}-issue-{issueNumber}-workspace-{timestamp}/repository` - for the cloned repo
69
+ - `/tmp/hive-mind-solve-gh-{owner}/{repo}-issue-{issueNumber}-workspace-{timestamp}/tmp` - for temp files, logs, downloads
70
+
71
+ The workspace tmp directory is passed to all tool prompts, with explicit examples for saving CI logs, diffs, and command outputs.
72
+
73
+ - Add relative time display for usage limit reset messages in GitHub comments
74
+
75
+ When the AI tool hits its usage limit, GitHub comments now show the reset time in a more user-friendly format:
76
+ - Before: `11:00 PM`
77
+ - After: `in 1h 23m (11:00 PM UTC)`
78
+
79
+ This helps users in different timezones understand when the limit will reset more quickly.
80
+
3
81
  ## 1.0.5
4
82
 
5
83
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.0.5",
3
+ "version": "1.2.0",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
package/src/agent.lib.mjs CHANGED
@@ -245,7 +245,7 @@ export const handleAgentRuntimeSwitch = async () => {
245
245
 
246
246
  // Main function to execute Agent with prompts and settings
247
247
  export const executeAgent = async params => {
248
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, agentPath = 'agent', $ } = params;
248
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, agentPath = 'agent', $ } = params;
249
249
 
250
250
  // Import prompt building functions from agent.prompts.lib.mjs
251
251
  const { buildUserPrompt, buildSystemPrompt } = await import('./agent.prompts.lib.mjs');
@@ -258,6 +258,7 @@ export const executeAgent = async params => {
258
258
  prUrl,
259
259
  branchName,
260
260
  tempDir,
261
+ workspaceTmpDir,
261
262
  isContinueMode,
262
263
  mergeStateStatus,
263
264
  forkedRepo,
@@ -276,6 +277,7 @@ export const executeAgent = async params => {
276
277
  prNumber,
277
278
  branchName,
278
279
  tempDir,
280
+ workspaceTmpDir,
279
281
  isContinueMode,
280
282
  forkedRepo,
281
283
  argv,
@@ -11,7 +11,7 @@ import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mj
11
11
  * @returns {string} The formatted user prompt
12
12
  */
13
13
  export const buildUserPrompt = params => {
14
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
14
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
15
15
 
16
16
  const promptLines = [];
17
17
 
@@ -26,6 +26,11 @@ export const buildUserPrompt = params => {
26
26
  promptLines.push(`Your prepared branch: ${branchName}`);
27
27
  promptLines.push(`Your prepared working directory: ${tempDir}`);
28
28
 
29
+ // Workspace tmp directory for logs and temp files (when --enable-workspaces is used)
30
+ if (workspaceTmpDir) {
31
+ promptLines.push(`Your prepared tmp directory for logs and downloads: ${workspaceTmpDir}`);
32
+ }
33
+
29
34
  // PR info if available
30
35
  if (prUrl) {
31
36
  promptLines.push(`Your prepared Pull Request: ${prUrl}`);
@@ -76,7 +81,7 @@ export const buildUserPrompt = params => {
76
81
  * @returns {string} The formatted system prompt
77
82
  */
78
83
  export const buildSystemPrompt = params => {
79
- const { owner, repo, issueNumber, prNumber, branchName, argv } = params;
84
+ const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv } = params;
80
85
 
81
86
  // Build thinking instruction based on --think level
82
87
  let thinkLine = '';
@@ -90,9 +95,42 @@ export const buildSystemPrompt = params => {
90
95
  thinkLine = `\n${thinkMessages[argv.think]}\n`;
91
96
  }
92
97
 
93
- return `You are AI issue solver using @link-assistant/agent.${thinkLine}
98
+ // Build workspace-specific instructions and examples
99
+ let workspaceInstructions = '';
100
+ if (workspaceTmpDir) {
101
+ workspaceInstructions = `
102
+ Workspace tmp directory.
103
+ - Use ${workspaceTmpDir} for all temporary files, logs, and downloads.
104
+ - When saving command output to files, save to ${workspaceTmpDir}/command-output.log.
105
+ - When downloading CI logs, save to ${workspaceTmpDir}/ci-logs/.
106
+ - When saving diffs for review, save to ${workspaceTmpDir}/diffs/.
107
+ - When creating debug files, save to ${workspaceTmpDir}/debug/.
108
+
109
+ `;
110
+ }
94
111
 
95
- General guidelines.
112
+ // Build CI command examples with workspace tmp paths
113
+ let ciExamples = '';
114
+ if (workspaceTmpDir) {
115
+ ciExamples = `
116
+ CI investigation with workspace tmp directory.
117
+ - When downloading CI run logs:
118
+ gh run view RUN_ID --repo ${owner}/${repo} --log > ${workspaceTmpDir}/ci-logs/run-RUN_ID.log
119
+ - When downloading failed job logs:
120
+ gh run view RUN_ID --repo ${owner}/${repo} --log-failed > ${workspaceTmpDir}/ci-logs/run-RUN_ID-failed.log
121
+ - When listing CI runs with details:
122
+ gh run list --repo ${owner}/${repo} --branch ${branchName} --limit 5 --json databaseId,conclusion,createdAt,headSha > ${workspaceTmpDir}/ci-logs/recent-runs.json
123
+ - When saving PR diff for review:
124
+ gh pr diff ${prNumber} --repo ${owner}/${repo} > ${workspaceTmpDir}/diffs/pr-${prNumber}.diff
125
+ - When saving command output with stderr:
126
+ npm test 2>&1 | tee ${workspaceTmpDir}/test-output.log
127
+ - When investigating issue details:
128
+ gh issue view ${issueNumber} --repo ${owner}/${repo} --json body,comments > ${workspaceTmpDir}/issue-${issueNumber}.json
129
+ `;
130
+ }
131
+
132
+ return `You are AI issue solver using @link-assistant/agent.${thinkLine}
133
+ ${workspaceInstructions}General guidelines.
96
134
  - When you execute commands, always save their logs to files for easier reading if the output becomes large.
97
135
  - When running commands, do not set a timeout yourself — let them run as long as needed.
98
136
  - When running sudo commands (especially package installations), always run them in the background to avoid timeout issues.
@@ -185,7 +223,7 @@ GitHub CLI command patterns.
185
223
  - When adding PR comment, use gh pr comment NUMBER --body "text" --repo OWNER/REPO.
186
224
  - When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
187
225
  - When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
188
- - When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${getArchitectureCareSubPrompt(argv)}`;
226
+ - When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
189
227
  };
190
228
 
191
229
  // Export all functions as default object too
@@ -413,7 +413,7 @@ export const checkPlaywrightMcpAvailability = async () => {
413
413
  * @returns {Object} Result of the execution including success status and session info
414
414
  */
415
415
  export const executeClaude = async params => {
416
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, setLogFile, getLogFile, formatAligned, getResourceSnapshot, claudePath, $ } = params;
416
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, setLogFile, getLogFile, formatAligned, getResourceSnapshot, claudePath, $ } = params;
417
417
  // Import prompt building functions from claude.prompts.lib.mjs
418
418
  const { buildUserPrompt, buildSystemPrompt } = await import('./claude.prompts.lib.mjs');
419
419
  // Build the user prompt
@@ -424,6 +424,7 @@ export const executeClaude = async params => {
424
424
  prUrl,
425
425
  branchName,
426
426
  tempDir,
427
+ workspaceTmpDir,
427
428
  isContinueMode,
428
429
  mergeStateStatus,
429
430
  forkedRepo,
@@ -443,6 +444,7 @@ export const executeClaude = async params => {
443
444
  prUrl,
444
445
  branchName,
445
446
  tempDir,
447
+ workspaceTmpDir,
446
448
  isContinueMode,
447
449
  forkedRepo,
448
450
  argv,
@@ -11,7 +11,7 @@ import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mj
11
11
  * @returns {string} The formatted user prompt
12
12
  */
13
13
  export const buildUserPrompt = params => {
14
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines } = params;
14
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines } = params;
15
15
 
16
16
  const promptLines = [];
17
17
 
@@ -26,6 +26,11 @@ export const buildUserPrompt = params => {
26
26
  promptLines.push(`Your prepared branch: ${branchName}`);
27
27
  promptLines.push(`Your prepared working directory: ${tempDir}`);
28
28
 
29
+ // Workspace tmp directory for logs and temp files (when --enable-workspaces is used)
30
+ if (workspaceTmpDir) {
31
+ promptLines.push(`Your prepared tmp directory for logs and downloads: ${workspaceTmpDir}`);
32
+ }
33
+
29
34
  // PR info if available
30
35
  if (prUrl) {
31
36
  promptLines.push(`Your prepared Pull Request: ${prUrl}`);
@@ -82,7 +87,7 @@ export const buildUserPrompt = params => {
82
87
  * @returns {string} The formatted system prompt
83
88
  */
84
89
  export const buildSystemPrompt = params => {
85
- const { owner, repo, issueNumber, prNumber, branchName, argv } = params;
90
+ const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv } = params;
86
91
 
87
92
  // Build thinking instruction based on --think level
88
93
  let thinkLine = '';
@@ -96,10 +101,44 @@ export const buildSystemPrompt = params => {
96
101
  thinkLine = `\n${thinkMessages[argv.think]}\n`;
97
102
  }
98
103
 
104
+ // Build workspace-specific instructions and examples
105
+ let workspaceInstructions = '';
106
+ if (workspaceTmpDir) {
107
+ workspaceInstructions = `
108
+ Workspace tmp directory.
109
+ - Use ${workspaceTmpDir} for all temporary files, logs, and downloads.
110
+ - When saving command output to files, save to ${workspaceTmpDir}/command-output.log.
111
+ - When downloading CI logs, save to ${workspaceTmpDir}/ci-logs/.
112
+ - When saving diffs for review, save to ${workspaceTmpDir}/diffs/.
113
+ - When creating debug files, save to ${workspaceTmpDir}/debug/.
114
+
115
+ `;
116
+ }
117
+
118
+ // Build CI command examples with workspace tmp paths
119
+ let ciExamples = '';
120
+ if (workspaceTmpDir) {
121
+ ciExamples = `
122
+ CI investigation with workspace tmp directory.
123
+ - When downloading CI run logs:
124
+ gh run view RUN_ID --repo ${owner}/${repo} --log > ${workspaceTmpDir}/ci-logs/run-RUN_ID.log
125
+ - When downloading failed job logs:
126
+ gh run view RUN_ID --repo ${owner}/${repo} --log-failed > ${workspaceTmpDir}/ci-logs/run-RUN_ID-failed.log
127
+ - When listing CI runs with details:
128
+ gh run list --repo ${owner}/${repo} --branch ${branchName} --limit 5 --json databaseId,conclusion,createdAt,headSha > ${workspaceTmpDir}/ci-logs/recent-runs.json
129
+ - When saving PR diff for review:
130
+ gh pr diff ${prNumber} --repo ${owner}/${repo} > ${workspaceTmpDir}/diffs/pr-${prNumber}.diff
131
+ - When saving command output with stderr:
132
+ npm test 2>&1 | tee ${workspaceTmpDir}/test-output.log
133
+ - When investigating issue details:
134
+ gh issue view ${issueNumber} --repo ${owner}/${repo} --json body,comments > ${workspaceTmpDir}/issue-${issueNumber}.json
135
+
136
+ `;
137
+ }
138
+
99
139
  // Use backticks for jq commands to avoid quote escaping issues
100
140
  return `You are an AI issue solver. You prefer to find the root cause of each and every issue. When you talk, you prefer to speak with facts which you have double-checked yourself or cite sources that provide evidence, like quote actual code or give references to documents or pages found on the internet. You are polite and patient, and prefer to assume good intent, trying your best to be helpful. If you are unsure or have assumptions, you prefer to test them yourself or ask questions to clarify requirements.${thinkLine}
101
-
102
- General guidelines.
141
+ ${workspaceInstructions}General guidelines.
103
142
  - When you execute commands, always save their logs to files for easier reading if the output becomes large.
104
143
  - When running commands, do not set a timeout yourself — let them run as long as needed (default timeout - 2 minutes is more than enough), and once they finish, review the logs in the file.
105
144
  - When running sudo commands (especially package installations like apt-get, yum, npm install, etc.), always run them in the background to avoid timeout issues and permission errors when the process needs to be killed. Use the run_in_background parameter or append & to the command.${
@@ -243,7 +282,7 @@ Plan sub-agent usage.
243
282
  - When using the Plan sub-agent, you can add it as the first item in your todo list.
244
283
  - When you delegate planning, use the Task tool with subagent_type="Plan" before starting implementation work.`
245
284
  : ''
246
- }${getArchitectureCareSubPrompt(argv)}`;
285
+ }${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
247
286
  };
248
287
 
249
288
  // Export all functions as default object too
package/src/codex.lib.mjs CHANGED
@@ -115,7 +115,7 @@ export const handleCodexRuntimeSwitch = async () => {
115
115
 
116
116
  // Main function to execute Codex with prompts and settings
117
117
  export const executeCodex = async params => {
118
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, codexPath = 'codex', $ } = params;
118
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, codexPath = 'codex', $ } = params;
119
119
 
120
120
  // Import prompt building functions from codex.prompts.lib.mjs
121
121
  const { buildUserPrompt, buildSystemPrompt } = await import('./codex.prompts.lib.mjs');
@@ -128,6 +128,7 @@ export const executeCodex = async params => {
128
128
  prUrl,
129
129
  branchName,
130
130
  tempDir,
131
+ workspaceTmpDir,
131
132
  isContinueMode,
132
133
  mergeStateStatus,
133
134
  forkedRepo,
@@ -146,6 +147,7 @@ export const executeCodex = async params => {
146
147
  prNumber,
147
148
  branchName,
148
149
  tempDir,
150
+ workspaceTmpDir,
149
151
  isContinueMode,
150
152
  forkedRepo,
151
153
  argv,
@@ -11,7 +11,7 @@ import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mj
11
11
  * @returns {string} The formatted user prompt
12
12
  */
13
13
  export const buildUserPrompt = params => {
14
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
14
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
15
15
 
16
16
  const promptLines = [];
17
17
 
@@ -26,6 +26,11 @@ export const buildUserPrompt = params => {
26
26
  promptLines.push(`Your prepared branch: ${branchName}`);
27
27
  promptLines.push(`Your prepared working directory: ${tempDir}`);
28
28
 
29
+ // Workspace tmp directory for logs and temp files (when --enable-workspaces is used)
30
+ if (workspaceTmpDir) {
31
+ promptLines.push(`Your prepared tmp directory for logs and downloads: ${workspaceTmpDir}`);
32
+ }
33
+
29
34
  // PR info if available
30
35
  if (prUrl) {
31
36
  promptLines.push(`Your prepared Pull Request: ${prUrl}`);
@@ -76,7 +81,7 @@ export const buildUserPrompt = params => {
76
81
  * @returns {string} The formatted system prompt
77
82
  */
78
83
  export const buildSystemPrompt = params => {
79
- const { owner, repo, issueNumber, prNumber, branchName, argv } = params;
84
+ const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv } = params;
80
85
 
81
86
  // Build thinking instruction based on --think level
82
87
  let thinkLine = '';
@@ -90,9 +95,42 @@ export const buildSystemPrompt = params => {
90
95
  thinkLine = `\n${thinkMessages[argv.think]}\n`;
91
96
  }
92
97
 
93
- return `You are AI issue solver using OpenAI Codex.${thinkLine}
98
+ // Build workspace-specific instructions and examples
99
+ let workspaceInstructions = '';
100
+ if (workspaceTmpDir) {
101
+ workspaceInstructions = `
102
+ Workspace tmp directory.
103
+ - Use ${workspaceTmpDir} for all temporary files, logs, and downloads.
104
+ - When saving command output to files, save to ${workspaceTmpDir}/command-output.log.
105
+ - When downloading CI logs, save to ${workspaceTmpDir}/ci-logs/.
106
+ - When saving diffs for review, save to ${workspaceTmpDir}/diffs/.
107
+ - When creating debug files, save to ${workspaceTmpDir}/debug/.
108
+
109
+ `;
110
+ }
94
111
 
95
- General guidelines.
112
+ // Build CI command examples with workspace tmp paths
113
+ let ciExamples = '';
114
+ if (workspaceTmpDir) {
115
+ ciExamples = `
116
+ CI investigation with workspace tmp directory.
117
+ - When downloading CI run logs:
118
+ gh run view RUN_ID --repo ${owner}/${repo} --log > ${workspaceTmpDir}/ci-logs/run-RUN_ID.log
119
+ - When downloading failed job logs:
120
+ gh run view RUN_ID --repo ${owner}/${repo} --log-failed > ${workspaceTmpDir}/ci-logs/run-RUN_ID-failed.log
121
+ - When listing CI runs with details:
122
+ gh run list --repo ${owner}/${repo} --branch ${branchName} --limit 5 --json databaseId,conclusion,createdAt,headSha > ${workspaceTmpDir}/ci-logs/recent-runs.json
123
+ - When saving PR diff for review:
124
+ gh pr diff ${prNumber} --repo ${owner}/${repo} > ${workspaceTmpDir}/diffs/pr-${prNumber}.diff
125
+ - When saving command output with stderr:
126
+ npm test 2>&1 | tee ${workspaceTmpDir}/test-output.log
127
+ - When investigating issue details:
128
+ gh issue view ${issueNumber} --repo ${owner}/${repo} --json body,comments > ${workspaceTmpDir}/issue-${issueNumber}.json
129
+ `;
130
+ }
131
+
132
+ return `You are AI issue solver using OpenAI Codex.${thinkLine}
133
+ ${workspaceInstructions}General guidelines.
96
134
  - When you execute commands, always save their logs to files for easier reading if the output becomes large.
97
135
  - When running commands, do not set a timeout yourself — let them run as long as needed (default timeout - 2 minutes is more than enough), and once they finish, review the logs in the file.
98
136
  - When running sudo commands (especially package installations like apt-get, yum, npm install, etc.), always run them in the background to avoid timeout issues and permission errors when the process needs to be killed. Use the run_in_background parameter or append & to the command.
@@ -193,7 +231,7 @@ GitHub CLI command patterns.
193
231
  - When adding PR comment, use gh pr comment NUMBER --body "text" --repo OWNER/REPO.
194
232
  - When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
195
233
  - When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
196
- - When filtering with jq, use gh api repos/\${owner}/\${repo}/pulls/\${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${getArchitectureCareSubPrompt(argv)}`;
234
+ - When filtering with jq, use gh api repos/\${owner}/\${repo}/pulls/\${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
197
235
  };
198
236
 
199
237
  // Export all functions as default object too
@@ -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
+ }
@@ -109,7 +109,7 @@ export const handleOpenCodeRuntimeSwitch = async () => {
109
109
 
110
110
  // Main function to execute OpenCode with prompts and settings
111
111
  export const executeOpenCode = async params => {
112
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, opencodePath = 'opencode', $ } = params;
112
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, mergeStateStatus, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, log, formatAligned, getResourceSnapshot, opencodePath = 'opencode', $ } = params;
113
113
 
114
114
  // Import prompt building functions from opencode.prompts.lib.mjs
115
115
  const { buildUserPrompt, buildSystemPrompt } = await import('./opencode.prompts.lib.mjs');
@@ -122,6 +122,7 @@ export const executeOpenCode = async params => {
122
122
  prUrl,
123
123
  branchName,
124
124
  tempDir,
125
+ workspaceTmpDir,
125
126
  isContinueMode,
126
127
  mergeStateStatus,
127
128
  forkedRepo,
@@ -140,6 +141,7 @@ export const executeOpenCode = async params => {
140
141
  prNumber,
141
142
  branchName,
142
143
  tempDir,
144
+ workspaceTmpDir,
143
145
  isContinueMode,
144
146
  forkedRepo,
145
147
  argv,
@@ -11,7 +11,7 @@ import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mj
11
11
  * @returns {string} The formatted user prompt
12
12
  */
13
13
  export const buildUserPrompt = params => {
14
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
14
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
15
15
 
16
16
  const promptLines = [];
17
17
 
@@ -26,6 +26,11 @@ export const buildUserPrompt = params => {
26
26
  promptLines.push(`Your prepared branch: ${branchName}`);
27
27
  promptLines.push(`Your prepared working directory: ${tempDir}`);
28
28
 
29
+ // Workspace tmp directory for logs and temp files (when --enable-workspaces is used)
30
+ if (workspaceTmpDir) {
31
+ promptLines.push(`Your prepared tmp directory for logs and downloads: ${workspaceTmpDir}`);
32
+ }
33
+
29
34
  // PR info if available
30
35
  if (prUrl) {
31
36
  promptLines.push(`Your prepared Pull Request: ${prUrl}`);
@@ -76,7 +81,7 @@ export const buildUserPrompt = params => {
76
81
  * @returns {string} The formatted system prompt
77
82
  */
78
83
  export const buildSystemPrompt = params => {
79
- const { owner, repo, issueNumber, prNumber, branchName, argv } = params;
84
+ const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv } = params;
80
85
 
81
86
  // Build thinking instruction based on --think level
82
87
  let thinkLine = '';
@@ -90,6 +95,41 @@ export const buildSystemPrompt = params => {
90
95
  thinkLine = `\n${thinkMessages[argv.think]}\n`;
91
96
  }
92
97
 
98
+ // Build workspace-specific instructions and examples
99
+ let workspaceInstructions = '';
100
+ if (workspaceTmpDir) {
101
+ workspaceInstructions = `
102
+ Workspace tmp directory.
103
+ - Use ${workspaceTmpDir} for all temporary files, logs, and downloads.
104
+ - When saving command output to files, save to ${workspaceTmpDir}/command-output.log.
105
+ - When downloading CI logs, save to ${workspaceTmpDir}/ci-logs/.
106
+ - When saving diffs for review, save to ${workspaceTmpDir}/diffs/.
107
+ - When creating debug files, save to ${workspaceTmpDir}/debug/.
108
+
109
+ `;
110
+ }
111
+
112
+ // Build CI command examples with workspace tmp paths
113
+ let ciExamples = '';
114
+ if (workspaceTmpDir) {
115
+ ciExamples = `
116
+ CI investigation with workspace tmp directory.
117
+ - When downloading CI run logs:
118
+ gh run view RUN_ID --repo ${owner}/${repo} --log > ${workspaceTmpDir}/ci-logs/run-RUN_ID.log
119
+ - When downloading failed job logs:
120
+ gh run view RUN_ID --repo ${owner}/${repo} --log-failed > ${workspaceTmpDir}/ci-logs/run-RUN_ID-failed.log
121
+ - When listing CI runs with details:
122
+ gh run list --repo ${owner}/${repo} --branch ${branchName} --limit 5 --json databaseId,conclusion,createdAt,headSha > ${workspaceTmpDir}/ci-logs/recent-runs.json
123
+ - When saving PR diff for review:
124
+ gh pr diff ${prNumber} --repo ${owner}/${repo} > ${workspaceTmpDir}/diffs/pr-${prNumber}.diff
125
+ - When saving command output with stderr:
126
+ npm test 2>&1 | tee ${workspaceTmpDir}/test-output.log
127
+ - When investigating issue details:
128
+ gh issue view ${issueNumber} --repo ${owner}/${repo} --json body,comments > ${workspaceTmpDir}/issue-${issueNumber}.json
129
+
130
+ `;
131
+ }
132
+
93
133
  return `You are AI issue solver using OpenCode.${thinkLine}
94
134
 
95
135
  General guidelines.
@@ -103,7 +143,7 @@ General guidelines.
103
143
  - When testing your assumptions, use the experiment scripts, and add it to experiments folder.
104
144
  - When your experiments can show real world use case of the software, add it to examples folder.
105
145
  - When you face something extremely hard, use divide and conquer — it always helps.
106
-
146
+ ${workspaceInstructions}
107
147
  Initial research.
108
148
  - When you start, make sure you create detailed plan for yourself and follow your todo list step by step, make sure that as many points from these guidelines are added to your todo list to keep track of everything that can help you solve the issue with highest possible quality.
109
149
  - When you read issue, read all details and comments thoroughly.
@@ -184,7 +224,7 @@ GitHub CLI command patterns.
184
224
  - When adding PR comment, use gh pr comment NUMBER --body "text" --repo OWNER/REPO.
185
225
  - When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
186
226
  - When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
187
- - When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${getArchitectureCareSubPrompt(argv)}`;
227
+ - When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
188
228
  };
189
229
 
190
230
  // Export all functions as default object too
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 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');
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
- const claudePath = process.env.CLAUDE_PATH || 'claude';
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('/');
@@ -253,6 +253,16 @@ 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
+ })
261
+ .option('enable-workspaces', {
262
+ type: 'boolean',
263
+ description: 'Use separate workspace directory structure with repository/ and tmp/ folders. Works with all tools (claude, opencode, codex, agent). Experimental feature.',
264
+ default: false,
265
+ })
256
266
  .option('interactive-mode', {
257
267
  type: 'boolean',
258
268
  description: '[EXPERIMENTAL] Post Claude output as PR comments in real-time. Only supported for --tool claude.',
package/src/solve.mjs CHANGED
@@ -65,6 +65,9 @@ const { executeClaude } = claudeLib;
65
65
  const githubLinking = await import('./github-linking.lib.mjs');
66
66
  const { extractLinkedIssueNumber } = githubLinking;
67
67
 
68
+ const usageLimitLib = await import('./usage-limit.lib.mjs');
69
+ const { formatResetTimeWithRelative } = usageLimitLib;
70
+
68
71
  const errorHandlers = await import('./solve.error-handlers.lib.mjs');
69
72
  const { createUncaughtExceptionHandler, createUnhandledRejectionHandler, handleMainExecutionError } = errorHandlers;
70
73
 
@@ -233,7 +236,7 @@ if (argv.verbose) {
233
236
  await log(` Is Issue URL: ${!!isIssueUrl}`, { verbose: true });
234
237
  await log(` Is PR URL: ${!!isPrUrl}`, { verbose: true });
235
238
  }
236
- const claudePath = process.env.CLAUDE_PATH || 'claude';
239
+ const claudePath = argv.executeToolWithBun ? 'bunx claude' : process.env.CLAUDE_PATH || 'claude';
237
240
  // Note: owner, repo, and urlNumber are already extracted from validateGitHubUrl() above
238
241
  // The parseUrlComponents() call was removed as it had a bug with hash fragments (#issuecomment-xyz)
239
242
  // and the validation result already provides these values correctly parsed
@@ -508,7 +511,9 @@ if (isPrUrl) {
508
511
  await log(`📝 Issue mode: Working with issue #${issueNumber}`);
509
512
  }
510
513
  // Create or find temporary directory for cloning the repository
511
- const { tempDir } = await setupTempDirectory(argv);
514
+ // Pass workspace info for --enable-workspaces mode (works with all tools)
515
+ const workspaceInfo = argv.enableWorkspaces ? { owner, repo, issueNumber } : null;
516
+ const { tempDir, workspaceTmpDir } = await setupTempDirectory(argv, workspaceInfo);
512
517
  // Populate cleanup context for signal handlers
513
518
  cleanupContext.tempDir = tempDir;
514
519
  cleanupContext.argv = argv;
@@ -761,6 +766,7 @@ try {
761
766
  prUrl,
762
767
  branchName,
763
768
  tempDir,
769
+ workspaceTmpDir,
764
770
  isContinueMode,
765
771
  mergeStateStatus,
766
772
  forkedRepo,
@@ -789,6 +795,7 @@ try {
789
795
  prUrl,
790
796
  branchName,
791
797
  tempDir,
798
+ workspaceTmpDir,
792
799
  isContinueMode,
793
800
  mergeStateStatus,
794
801
  forkedRepo,
@@ -817,6 +824,7 @@ try {
817
824
  prUrl,
818
825
  branchName,
819
826
  tempDir,
827
+ workspaceTmpDir,
820
828
  isContinueMode,
821
829
  mergeStateStatus,
822
830
  forkedRepo,
@@ -858,6 +866,7 @@ try {
858
866
  prUrl,
859
867
  branchName,
860
868
  tempDir,
869
+ workspaceTmpDir,
861
870
  isContinueMode,
862
871
  mergeStateStatus,
863
872
  forkedRepo,
@@ -961,7 +970,9 @@ try {
961
970
  const tool = argv.tool || 'claude';
962
971
  const resumeCmd = tool === 'claude' ? buildClaudeResumeCommand({ tempDir, sessionId, model: argv.model }) : null;
963
972
  const resumeSection = resumeCmd ? `To resume after the limit resets, use:\n\`\`\`bash\n${resumeCmd}\n\`\`\`` : `Session ID: \`${sessionId}\``;
964
- const failureComment = resetTime ? `❌ **Usage Limit Reached**\n\nThe AI tool has reached its usage limit. The limit will reset at: **${resetTime}**\n\n${resumeSection}` : `❌ **Usage Limit Reached**\n\nThe AI tool has reached its usage limit. Please wait for the limit to reset.\n\n${resumeSection}`;
973
+ // Format the reset time with relative time if available
974
+ const formattedResetTime = resetTime ? formatResetTimeWithRelative(resetTime) : null;
975
+ const failureComment = formattedResetTime ? `❌ **Usage Limit Reached**\n\nThe AI tool has reached its usage limit. The limit will reset at: **${formattedResetTime}**\n\n${resumeSection}` : `❌ **Usage Limit Reached**\n\nThe AI tool has reached its usage limit. Please wait for the limit to reset.\n\n${resumeSection}`;
965
976
 
966
977
  const commentResult = await $`gh pr comment ${prNumber} --repo ${owner}/${repo} --body ${failureComment}`;
967
978
  if (commentResult.code === 0) {
@@ -199,11 +199,35 @@ export const validateForkParent = async (forkRepo, expectedUpstream) => {
199
199
  }
200
200
  };
201
201
 
202
+ /**
203
+ * Build workspace directory name according to the specification:
204
+ * /tmp/hive-mind-solve-gh-{owner}/{repo}-issue-{issueNumber}-workspace-{timestamp}
205
+ *
206
+ * @param {string} owner - Repository owner
207
+ * @param {string} repo - Repository name
208
+ * @param {number|string} issueNumber - Issue number
209
+ * @param {number} timestamp - Unix timestamp
210
+ * @returns {string} The workspace directory path
211
+ */
212
+ export const buildWorkspacePath = (owner, repo, issueNumber, timestamp) => {
213
+ // Format: /tmp/hive-mind-solve-gh-{owner}/{repo}-issue-{issueNumber}-workspace-{timestamp}
214
+ const baseDir = path.join(os.tmpdir(), `hive-mind-solve-gh-${owner}`);
215
+ const workspaceDir = path.join(baseDir, `${repo}-issue-${issueNumber}-workspace-${timestamp}`);
216
+ return workspaceDir;
217
+ };
218
+
202
219
  // Create or find temporary directory for cloning the repository
203
- export const setupTempDirectory = async argv => {
220
+ // When --enable-workspaces is used, creates:
221
+ // {workspace}/repository - for the cloned repo
222
+ // {workspace}/tmp - for temp files, logs, downloads
223
+ export const setupTempDirectory = async (argv, workspaceInfo = null) => {
204
224
  let tempDir;
225
+ let workspaceTmpDir = null;
205
226
  let isResuming = argv.resume;
206
227
 
228
+ // Check if workspace mode should be enabled (works with all tools)
229
+ const useWorkspaces = argv.enableWorkspaces;
230
+
207
231
  if (isResuming) {
208
232
  // When resuming, try to find existing directory or create a new one
209
233
  const scriptDir = path.dirname(process.argv[1]);
@@ -229,13 +253,34 @@ export const setupTempDirectory = async argv => {
229
253
  await fs.mkdir(tempDir, { recursive: true });
230
254
  await log(`Creating temporary directory for resumed session: ${tempDir}`);
231
255
  }
256
+ } else if (useWorkspaces && workspaceInfo) {
257
+ // Workspace mode: create structured workspace with repository/ and tmp/ subdirectories
258
+ const { owner, repo, issueNumber } = workspaceInfo;
259
+ const timestamp = Date.now();
260
+ const workspaceDir = buildWorkspacePath(owner, repo, issueNumber, timestamp);
261
+
262
+ // Create the workspace structure:
263
+ // {workspace}/repository - where the repo will be cloned
264
+ // {workspace}/tmp - for temp files, logs, command outputs
265
+ const repoDir = path.join(workspaceDir, 'repository');
266
+ workspaceTmpDir = path.join(workspaceDir, 'tmp');
267
+
268
+ await fs.mkdir(repoDir, { recursive: true });
269
+ await fs.mkdir(workspaceTmpDir, { recursive: true });
270
+
271
+ tempDir = repoDir;
272
+
273
+ await log(`\n${formatAligned('📂', 'Workspace mode:', 'ENABLED')}`);
274
+ await log(formatAligned('', 'Workspace root:', workspaceDir, 2));
275
+ await log(formatAligned('', 'Repository dir:', repoDir, 2));
276
+ await log(formatAligned('', 'Temp dir:', workspaceTmpDir, 2));
232
277
  } else {
233
278
  tempDir = path.join(os.tmpdir(), `gh-issue-solver-${Date.now()}`);
234
279
  await fs.mkdir(tempDir, { recursive: true });
235
280
  await log(`\nCreating temporary directory: ${tempDir}`);
236
281
  }
237
282
 
238
- return { tempDir, isResuming };
283
+ return { tempDir, workspaceTmpDir, isResuming };
239
284
  };
240
285
 
241
286
  // Try to initialize an empty repository by creating a simple README.md
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) => {
@@ -142,6 +142,111 @@ export function detectUsageLimit(message) {
142
142
  };
143
143
  }
144
144
 
145
+ /**
146
+ * Parse time string (e.g., "11:00 PM") and convert to Date object for today
147
+ *
148
+ * @param {string} timeStr - Time string in format "HH:MM AM/PM"
149
+ * @returns {Date|null} - Date object or null if parsing fails
150
+ */
151
+ export function parseResetTime(timeStr) {
152
+ if (!timeStr || typeof timeStr !== 'string') {
153
+ return null;
154
+ }
155
+
156
+ // Match pattern like "11:00 PM" or "11:00PM"
157
+ const match = timeStr.match(/(\d{1,2}):(\d{2})\s*(AM|PM)/i);
158
+ if (!match) {
159
+ return null;
160
+ }
161
+
162
+ let hour = parseInt(match[1], 10);
163
+ const minute = parseInt(match[2], 10);
164
+ const ampm = match[3].toUpperCase();
165
+
166
+ // Convert to 24-hour format
167
+ if (ampm === 'PM' && hour !== 12) {
168
+ hour += 12;
169
+ } else if (ampm === 'AM' && hour === 12) {
170
+ hour = 0;
171
+ }
172
+
173
+ // Create date for today with the parsed time
174
+ const now = new Date();
175
+ const resetDate = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hour, minute, 0, 0);
176
+
177
+ // If the time is in the past today, assume it's tomorrow
178
+ if (resetDate <= now) {
179
+ resetDate.setDate(resetDate.getDate() + 1);
180
+ }
181
+
182
+ return resetDate;
183
+ }
184
+
185
+ /**
186
+ * Format relative time (e.g., "in 1h 23m")
187
+ *
188
+ * @param {Date} resetDate - Date object for reset time
189
+ * @returns {string} - Formatted relative time string
190
+ */
191
+ export function formatRelativeTime(resetDate) {
192
+ if (!resetDate || !(resetDate instanceof Date)) {
193
+ return '';
194
+ }
195
+
196
+ const now = new Date();
197
+ const diffMs = resetDate - now;
198
+
199
+ if (diffMs <= 0) {
200
+ return 'now';
201
+ }
202
+
203
+ const totalSeconds = Math.floor(diffMs / 1000);
204
+ const totalMinutes = Math.floor(totalSeconds / 60);
205
+ const totalHours = Math.floor(totalMinutes / 60);
206
+ const totalDays = Math.floor(totalHours / 24);
207
+
208
+ const days = totalDays;
209
+ const hours = totalHours % 24;
210
+ const minutes = totalMinutes % 60;
211
+
212
+ const parts = [];
213
+ if (days > 0) parts.push(`${days}d`);
214
+ if (hours > 0) parts.push(`${hours}h`);
215
+ if (minutes > 0 || parts.length === 0) parts.push(`${minutes}m`);
216
+
217
+ return `in ${parts.join(' ')}`;
218
+ }
219
+
220
+ /**
221
+ * Format reset time with relative time and UTC time
222
+ * Example: "in 1h 23m (11:00 PM UTC)"
223
+ *
224
+ * @param {string} resetTime - Time string in format "HH:MM AM/PM"
225
+ * @returns {string} - Formatted string with relative and absolute time
226
+ */
227
+ export function formatResetTimeWithRelative(resetTime) {
228
+ if (!resetTime) {
229
+ return resetTime;
230
+ }
231
+
232
+ const resetDate = parseResetTime(resetTime);
233
+ if (!resetDate) {
234
+ // If we can't parse it, return the original time
235
+ return resetTime;
236
+ }
237
+
238
+ const relativeTime = formatRelativeTime(resetDate);
239
+
240
+ // Format the UTC time
241
+ const utcHours = resetDate.getUTCHours();
242
+ const utcMinutes = resetDate.getUTCMinutes();
243
+ const utcAmPm = utcHours >= 12 ? 'PM' : 'AM';
244
+ const utcHour12 = utcHours % 12 || 12;
245
+ const utcTimeStr = `${utcHour12}:${String(utcMinutes).padStart(2, '0')} ${utcAmPm} UTC`;
246
+
247
+ return `${relativeTime} (${utcTimeStr})`;
248
+ }
249
+
145
250
  /**
146
251
  * Format usage limit error message for console output
147
252
  *