@link-assistant/hive-mind 0.39.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 +20 -0
- package/LICENSE +24 -0
- package/README.md +769 -0
- package/package.json +58 -0
- package/src/agent.lib.mjs +705 -0
- package/src/agent.prompts.lib.mjs +196 -0
- package/src/buildUserMention.lib.mjs +71 -0
- package/src/claude-limits.lib.mjs +389 -0
- package/src/claude.lib.mjs +1445 -0
- package/src/claude.prompts.lib.mjs +203 -0
- package/src/codex.lib.mjs +552 -0
- package/src/codex.prompts.lib.mjs +194 -0
- package/src/config.lib.mjs +207 -0
- package/src/contributing-guidelines.lib.mjs +268 -0
- package/src/exit-handler.lib.mjs +205 -0
- package/src/git.lib.mjs +145 -0
- package/src/github-issue-creator.lib.mjs +246 -0
- package/src/github-linking.lib.mjs +152 -0
- package/src/github.batch.lib.mjs +272 -0
- package/src/github.graphql.lib.mjs +258 -0
- package/src/github.lib.mjs +1479 -0
- package/src/hive.config.lib.mjs +254 -0
- package/src/hive.mjs +1500 -0
- package/src/instrument.mjs +191 -0
- package/src/interactive-mode.lib.mjs +1000 -0
- package/src/lenv-reader.lib.mjs +206 -0
- package/src/lib.mjs +490 -0
- package/src/lino.lib.mjs +176 -0
- package/src/local-ci-checks.lib.mjs +324 -0
- package/src/memory-check.mjs +419 -0
- package/src/model-mapping.lib.mjs +145 -0
- package/src/model-validation.lib.mjs +278 -0
- package/src/opencode.lib.mjs +479 -0
- package/src/opencode.prompts.lib.mjs +194 -0
- package/src/protect-branch.mjs +159 -0
- package/src/review.mjs +433 -0
- package/src/reviewers-hive.mjs +643 -0
- package/src/sentry.lib.mjs +284 -0
- package/src/solve.auto-continue.lib.mjs +568 -0
- package/src/solve.auto-pr.lib.mjs +1374 -0
- package/src/solve.branch-errors.lib.mjs +341 -0
- package/src/solve.branch.lib.mjs +230 -0
- package/src/solve.config.lib.mjs +342 -0
- package/src/solve.error-handlers.lib.mjs +256 -0
- package/src/solve.execution.lib.mjs +291 -0
- package/src/solve.feedback.lib.mjs +436 -0
- package/src/solve.mjs +1128 -0
- package/src/solve.preparation.lib.mjs +210 -0
- package/src/solve.repo-setup.lib.mjs +114 -0
- package/src/solve.repository.lib.mjs +961 -0
- package/src/solve.results.lib.mjs +558 -0
- package/src/solve.session.lib.mjs +135 -0
- package/src/solve.validation.lib.mjs +325 -0
- package/src/solve.watch.lib.mjs +572 -0
- package/src/start-screen.mjs +324 -0
- package/src/task.mjs +308 -0
- package/src/telegram-bot.mjs +1481 -0
- package/src/telegram-markdown.lib.mjs +64 -0
- package/src/usage-limit.lib.mjs +218 -0
- package/src/version.lib.mjs +41 -0
- package/src/youtrack/solve.youtrack.lib.mjs +116 -0
- package/src/youtrack/youtrack-sync.mjs +219 -0
- package/src/youtrack/youtrack.lib.mjs +425 -0
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex prompts module
|
|
3
|
+
* Handles building prompts for Codex CLI commands
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Build the user prompt for Codex
|
|
8
|
+
* @param {Object} params - Parameters for building the user prompt
|
|
9
|
+
* @returns {string} The formatted user prompt
|
|
10
|
+
*/
|
|
11
|
+
export const buildUserPrompt = (params) => {
|
|
12
|
+
const {
|
|
13
|
+
issueUrl,
|
|
14
|
+
issueNumber,
|
|
15
|
+
prNumber,
|
|
16
|
+
prUrl,
|
|
17
|
+
branchName,
|
|
18
|
+
tempDir,
|
|
19
|
+
isContinueMode,
|
|
20
|
+
forkedRepo,
|
|
21
|
+
feedbackLines,
|
|
22
|
+
forkActionsUrl,
|
|
23
|
+
owner,
|
|
24
|
+
repo,
|
|
25
|
+
argv
|
|
26
|
+
} = params;
|
|
27
|
+
|
|
28
|
+
const promptLines = [];
|
|
29
|
+
|
|
30
|
+
// Issue or PR reference
|
|
31
|
+
if (isContinueMode) {
|
|
32
|
+
promptLines.push(`Issue to solve: ${issueNumber ? `https://github.com/${owner}/${repo}/issues/${issueNumber}` : `Issue linked to PR #${prNumber}`}`);
|
|
33
|
+
} else {
|
|
34
|
+
promptLines.push(`Issue to solve: ${issueUrl}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Basic info
|
|
38
|
+
promptLines.push(`Your prepared branch: ${branchName}`);
|
|
39
|
+
promptLines.push(`Your prepared working directory: ${tempDir}`);
|
|
40
|
+
|
|
41
|
+
// PR info if available
|
|
42
|
+
if (prUrl) {
|
|
43
|
+
promptLines.push(`Your prepared Pull Request: ${prUrl}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Fork info if applicable
|
|
47
|
+
if (argv && argv.fork && forkedRepo) {
|
|
48
|
+
promptLines.push(`Your forked repository: ${forkedRepo}`);
|
|
49
|
+
promptLines.push(`Original repository (upstream): ${owner}/${repo}`);
|
|
50
|
+
|
|
51
|
+
// Check for GitHub Actions on fork
|
|
52
|
+
if (branchName && forkActionsUrl) {
|
|
53
|
+
promptLines.push(`GitHub Actions on your fork: ${forkActionsUrl}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Add blank line
|
|
58
|
+
promptLines.push('');
|
|
59
|
+
|
|
60
|
+
// Add feedback info if in continue mode and there are feedback items
|
|
61
|
+
if (isContinueMode && feedbackLines && feedbackLines.length > 0) {
|
|
62
|
+
// Add each feedback line directly
|
|
63
|
+
feedbackLines.forEach(line => promptLines.push(line));
|
|
64
|
+
promptLines.push('');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add thinking instruction based on --think level
|
|
68
|
+
if (argv && argv.think) {
|
|
69
|
+
const thinkMessages = {
|
|
70
|
+
low: 'Think.',
|
|
71
|
+
medium: 'Think hard.',
|
|
72
|
+
high: 'Think harder.',
|
|
73
|
+
max: 'Ultrathink.'
|
|
74
|
+
};
|
|
75
|
+
promptLines.push(thinkMessages[argv.think]);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Final instruction
|
|
79
|
+
promptLines.push(isContinueMode ? 'Continue.' : 'Proceed.');
|
|
80
|
+
|
|
81
|
+
// Build the final prompt
|
|
82
|
+
return promptLines.join('\n');
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Build the system prompt for Codex - adapted for Codex's capabilities
|
|
87
|
+
* @param {Object} params - Parameters for building the prompt
|
|
88
|
+
* @returns {string} The formatted system prompt
|
|
89
|
+
*/
|
|
90
|
+
export const buildSystemPrompt = (params) => {
|
|
91
|
+
const { owner, repo, issueNumber, prNumber, branchName, argv } = params;
|
|
92
|
+
|
|
93
|
+
// Build thinking instruction based on --think level
|
|
94
|
+
let thinkLine = '';
|
|
95
|
+
if (argv && argv.think) {
|
|
96
|
+
const thinkMessages = {
|
|
97
|
+
low: 'You always think on every step.',
|
|
98
|
+
medium: 'You always think hard on every step.',
|
|
99
|
+
high: 'You always think harder on every step.',
|
|
100
|
+
max: 'You always ultrathink on every step.'
|
|
101
|
+
};
|
|
102
|
+
thinkLine = `\n${thinkMessages[argv.think]}\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return `You are AI issue solver using OpenAI Codex.${thinkLine}
|
|
106
|
+
|
|
107
|
+
General guidelines.
|
|
108
|
+
- When you execute commands, always save their logs to files for easier reading if the output becomes large.
|
|
109
|
+
- 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.
|
|
110
|
+
- 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.
|
|
111
|
+
- When CI is failing or user reports failures, consider adding a detailed investigation protocol to your todo list with these steps:
|
|
112
|
+
Step 1: List recent runs with timestamps using: gh run list --repo ${owner}/${repo} --branch ${branchName} --limit 5 --json databaseId,conclusion,createdAt,headSha
|
|
113
|
+
Step 2: Verify runs are after the latest commit by checking timestamps and SHA
|
|
114
|
+
Step 3: For each non-passing run, download logs to preserve them: gh run view {run-id} --repo ${owner}/${repo} --log > ci-logs/{workflow}-{run-id}.log
|
|
115
|
+
Step 4: Read each downloaded log file using Read tool to understand the actual failures
|
|
116
|
+
Step 5: Report findings with specific errors and line numbers from logs
|
|
117
|
+
This detailed investigation is especially helpful when user mentions CI failures, asks to investigate logs, you see non-passing status, or when finalizing a PR.
|
|
118
|
+
Note: If user says "failing" but tools show "passing", this might indicate stale data - consider downloading fresh logs and checking timestamps to resolve the discrepancy.
|
|
119
|
+
- When a code or log file has more than 1500 lines, read it in chunks of 1500 lines.
|
|
120
|
+
- When facing a complex problem, do as much tracing as possible and turn on all verbose modes.
|
|
121
|
+
- When you create debug, test, or example/experiment scripts for fixing, always keep them in an examples and/or experiments folders so you can reuse them later.
|
|
122
|
+
- When testing your assumptions, use the experiment scripts, and add it to experiments folder.
|
|
123
|
+
- When your experiments can show real world use case of the software, add it to examples folder.
|
|
124
|
+
- When you face something extremely hard, use divide and conquer — it always helps.
|
|
125
|
+
|
|
126
|
+
Initial research.
|
|
127
|
+
- 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.
|
|
128
|
+
- When user mentions CI failures or asks to investigate logs, consider adding these todos to track the investigation: (1) List recent CI runs with timestamps, (2) Download logs from failed runs to ci-logs/ directory, (3) Analyze error messages and identify root cause, (4) Implement fix, (5) Verify fix resolves the specific errors found in logs.
|
|
129
|
+
- When you read issue, read all details and comments thoroughly.
|
|
130
|
+
- When you see screenshots or images in issue descriptions, pull request descriptions, comments, or discussions, use WebFetch tool (or fetch tool) to download the image first, then use Read tool to view and analyze it.
|
|
131
|
+
- When you need issue details, use gh issue view https://github.com/${owner}/${repo}/issues/${issueNumber}.
|
|
132
|
+
- When you need related code, use gh search code --owner ${owner} [keywords].
|
|
133
|
+
- When you need repo context, read files in your working directory.
|
|
134
|
+
- When you study related work, study the most recent related pull requests.
|
|
135
|
+
- When issue is not defined enough, write a comment to ask clarifying questions.
|
|
136
|
+
- When accessing GitHub Gists (especially private ones), use gh gist view command instead of direct URL fetching to ensure proper authentication.
|
|
137
|
+
- When you are fixing a bug, please make sure you first find the actual root cause, do as many experiments as needed.
|
|
138
|
+
- When you are fixing a bug and code does not have enough tracing/logs, add them and make sure they stay in the code, but are switched off by default.
|
|
139
|
+
- When you need latest comments on pull request (sorted newest first), use appropriate GitHub API commands.
|
|
140
|
+
- When you need latest comments on issue (sorted newest first), use appropriate GitHub API commands.
|
|
141
|
+
|
|
142
|
+
Solution development and testing.
|
|
143
|
+
- When issue is solvable, implement code with tests.
|
|
144
|
+
- When coding, each atomic step that can be useful by itself should be commited to the pull request's branch, meaning if work will be interrupted by any reason parts of solution will still be kept intact and safe in pull request.
|
|
145
|
+
- When you test:
|
|
146
|
+
start from testing of small functions using separate scripts;
|
|
147
|
+
write unit tests with mocks for easy and quick start.
|
|
148
|
+
- When you test integrations, use existing framework.
|
|
149
|
+
- When you test solution draft, include automated checks in pr.
|
|
150
|
+
- When issue is unclear, write comment on issue asking questions.
|
|
151
|
+
- When you encounter any problems that you unable to solve yourself (any human feedback or help), write a comment to the pull request asking for help.
|
|
152
|
+
- When you need human help, use gh pr comment ${prNumber} --body "your message" to comment on existing PR.
|
|
153
|
+
|
|
154
|
+
Preparing pull request.
|
|
155
|
+
- When you code, follow contributing guidelines.
|
|
156
|
+
- When you commit, write clear message.
|
|
157
|
+
- When you need examples of style, use gh pr list --repo ${owner}/${repo} --state merged --search [keywords].
|
|
158
|
+
- When you open pr, describe solution draft and include tests.
|
|
159
|
+
- When there is a package with version and GitHub Actions workflows for automatic release, update the version (or other necessary release trigger) in your pull request to prepare for next release.
|
|
160
|
+
- When you update existing pr ${prNumber}, use gh pr edit to modify title and description.
|
|
161
|
+
- When you are about to commit or push code, ALWAYS run local CI checks first if they are available in contributing guidelines (like ruff check, mypy, eslint, etc.) to catch errors before pushing.
|
|
162
|
+
- When you finalize the pull request:
|
|
163
|
+
follow style from merged prs for code, title, and description,
|
|
164
|
+
make sure no uncommitted changes corresponding to the original requirements are left behind,
|
|
165
|
+
make sure the default branch is merged to the pull request's branch,
|
|
166
|
+
make sure all CI checks passing if they exist before you finish,
|
|
167
|
+
double-check that all changes in the pull request answer to original requirements of the issue,
|
|
168
|
+
make sure no new new bugs are introduced in pull request by carefully reading gh pr diff,
|
|
169
|
+
make sure no previously existing features were removed without an explicit request from users via the issue description, issue comments, and/or pull request comments.
|
|
170
|
+
- When you finish implementation, use gh pr ready ${prNumber}.
|
|
171
|
+
|
|
172
|
+
Workflow and collaboration.
|
|
173
|
+
- When you check branch, verify with git branch --show-current.
|
|
174
|
+
- When you push, push only to branch ${branchName}.
|
|
175
|
+
- When you finish, create a pull request from branch ${branchName}. (Note: PR ${prNumber} already exists, update it instead)
|
|
176
|
+
- When you organize workflow, use pull requests instead of direct merges to default branch (main or master).
|
|
177
|
+
- When you manage commits, preserve commit history for later analysis.
|
|
178
|
+
- When you contribute, keep repository history forward-moving with regular commits, pushes, and reverts if needed.
|
|
179
|
+
- When you face conflict that you cannot resolve yourself, ask for help.
|
|
180
|
+
- When you collaborate, respect branch protections by working only on ${branchName}.
|
|
181
|
+
- When you mention result, include pull request url or comment url.
|
|
182
|
+
- When you need to create pr, remember pr ${prNumber} already exists for this branch.
|
|
183
|
+
|
|
184
|
+
Self review.
|
|
185
|
+
- When you check your solution draft, run all tests locally.
|
|
186
|
+
- When you compare with repo style, use gh pr diff [number].
|
|
187
|
+
- When you finalize, confirm code, tests, and description are consistent.`;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// Export all functions as default object too
|
|
191
|
+
export default {
|
|
192
|
+
buildUserPrompt,
|
|
193
|
+
buildSystemPrompt
|
|
194
|
+
};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Central configuration module for all configurable values
|
|
5
|
+
* Provides environment variable overrides with sensible defaults
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// Use use-m to dynamically import modules
|
|
9
|
+
if (typeof globalThis.use === 'undefined') {
|
|
10
|
+
try {
|
|
11
|
+
globalThis.use = (await eval(await (await fetch('https://unpkg.com/use-m/use.js')).text())).use;
|
|
12
|
+
} catch (error) {
|
|
13
|
+
console.error('❌ Fatal error: Failed to load dependencies for configuration');
|
|
14
|
+
console.error(` ${error.message}`);
|
|
15
|
+
console.error(' This might be due to network issues or missing dependencies.');
|
|
16
|
+
console.error(' Please check your internet connection and try again.');
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const getenv = await use('getenv');
|
|
22
|
+
|
|
23
|
+
// Import lino for parsing Links Notation format
|
|
24
|
+
const { lino } = await import('./lino.lib.mjs');
|
|
25
|
+
|
|
26
|
+
// Helper function to safely parse integers with fallback
|
|
27
|
+
const parseIntWithDefault = (envVar, defaultValue) => {
|
|
28
|
+
const value = getenv(envVar, defaultValue.toString());
|
|
29
|
+
const parsed = parseInt(value);
|
|
30
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Helper function to safely parse floats with fallback
|
|
34
|
+
const parseFloatWithDefault = (envVar, defaultValue) => {
|
|
35
|
+
const value = getenv(envVar, defaultValue.toString());
|
|
36
|
+
const parsed = parseFloat(value);
|
|
37
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Timeout configurations (in milliseconds)
|
|
41
|
+
export const timeouts = {
|
|
42
|
+
claudeCli: parseIntWithDefault('HIVE_MIND_CLAUDE_TIMEOUT_SECONDS', 60) * 1000,
|
|
43
|
+
opencodeCli: parseIntWithDefault('HIVE_MIND_OPENCODE_TIMEOUT_SECONDS', 60) * 1000,
|
|
44
|
+
codexCli: parseIntWithDefault('HIVE_MIND_CODEX_TIMEOUT_SECONDS', 60) * 1000,
|
|
45
|
+
githubApiDelay: parseIntWithDefault('HIVE_MIND_GITHUB_API_DELAY_MS', 5000),
|
|
46
|
+
githubRepoDelay: parseIntWithDefault('HIVE_MIND_GITHUB_REPO_DELAY_MS', 2000),
|
|
47
|
+
retryBaseDelay: parseIntWithDefault('HIVE_MIND_RETRY_BASE_DELAY_MS', 5000),
|
|
48
|
+
retryBackoffDelay: parseIntWithDefault('HIVE_MIND_RETRY_BACKOFF_DELAY_MS', 1000),
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Auto-continue configurations
|
|
52
|
+
export const autoContinue = {
|
|
53
|
+
ageThresholdHours: parseIntWithDefault('HIVE_MIND_AUTO_CONTINUE_AGE_HOURS', 24),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// GitHub API limits
|
|
57
|
+
export const githubLimits = {
|
|
58
|
+
commentMaxSize: parseIntWithDefault('HIVE_MIND_GITHUB_COMMENT_MAX_SIZE', 65536),
|
|
59
|
+
fileMaxSize: parseIntWithDefault('HIVE_MIND_GITHUB_FILE_MAX_SIZE', 25 * 1024 * 1024),
|
|
60
|
+
issueBodyMaxSize: parseIntWithDefault('HIVE_MIND_GITHUB_ISSUE_BODY_MAX_SIZE', 60000),
|
|
61
|
+
attachmentMaxSize: parseIntWithDefault('HIVE_MIND_GITHUB_ATTACHMENT_MAX_SIZE', 10 * 1024 * 1024),
|
|
62
|
+
bufferMaxSize: parseIntWithDefault('HIVE_MIND_GITHUB_BUFFER_MAX_SIZE', 10 * 1024 * 1024),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Memory and disk configurations
|
|
66
|
+
export const systemLimits = {
|
|
67
|
+
minDiskSpaceMb: parseIntWithDefault('HIVE_MIND_MIN_DISK_SPACE_MB', 500),
|
|
68
|
+
defaultPageSizeKb: parseIntWithDefault('HIVE_MIND_DEFAULT_PAGE_SIZE_KB', 16),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Retry configurations
|
|
72
|
+
export const retryLimits = {
|
|
73
|
+
maxForkRetries: parseIntWithDefault('HIVE_MIND_MAX_FORK_RETRIES', 5),
|
|
74
|
+
maxVerifyRetries: parseIntWithDefault('HIVE_MIND_MAX_VERIFY_RETRIES', 5),
|
|
75
|
+
maxApiRetries: parseIntWithDefault('HIVE_MIND_MAX_API_RETRIES', 3),
|
|
76
|
+
retryBackoffMultiplier: parseFloatWithDefault('HIVE_MIND_RETRY_BACKOFF_MULTIPLIER', 2),
|
|
77
|
+
max503Retries: parseIntWithDefault('HIVE_MIND_MAX_503_RETRIES', 3),
|
|
78
|
+
initial503RetryDelayMs: parseIntWithDefault('HIVE_MIND_INITIAL_503_RETRY_DELAY_MS', 5 * 60 * 1000), // 5 minutes
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// File and path configurations
|
|
82
|
+
export const filePaths = {
|
|
83
|
+
tempDir: getenv('HIVE_MIND_TEMP_DIR', '/tmp'),
|
|
84
|
+
taskInfoFilename: getenv('HIVE_MIND_TASK_INFO_FILENAME', 'CLAUDE.md'),
|
|
85
|
+
procMeminfo: getenv('HIVE_MIND_PROC_MEMINFO', '/proc/meminfo'),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Text processing configurations
|
|
89
|
+
export const textProcessing = {
|
|
90
|
+
tokenMaskMinLength: parseIntWithDefault('HIVE_MIND_TOKEN_MASK_MIN_LENGTH', 12),
|
|
91
|
+
tokenMaskStartChars: parseIntWithDefault('HIVE_MIND_TOKEN_MASK_START_CHARS', 5),
|
|
92
|
+
tokenMaskEndChars: parseIntWithDefault('HIVE_MIND_TOKEN_MASK_END_CHARS', 5),
|
|
93
|
+
textPreviewLength: parseIntWithDefault('HIVE_MIND_TEXT_PREVIEW_LENGTH', 100),
|
|
94
|
+
logTruncationLength: parseIntWithDefault('HIVE_MIND_LOG_TRUNCATION_LENGTH', 5000),
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// UI/Display configurations
|
|
98
|
+
export const display = {
|
|
99
|
+
labelWidth: parseIntWithDefault('HIVE_MIND_LABEL_WIDTH', 25),
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Sentry configurations
|
|
103
|
+
export const sentry = {
|
|
104
|
+
dsn: getenv('HIVE_MIND_SENTRY_DSN', 'https://77b711f23c84cbf74366df82090dc389@o4510072519983104.ingest.us.sentry.io/4510072523325440'),
|
|
105
|
+
tracesSampleRateDev: parseFloatWithDefault('HIVE_MIND_SENTRY_TRACES_SAMPLE_RATE_DEV', 1.0),
|
|
106
|
+
tracesSampleRateProd: parseFloatWithDefault('HIVE_MIND_SENTRY_TRACES_SAMPLE_RATE_PROD', 0.1),
|
|
107
|
+
profileSessionSampleRateDev: parseFloatWithDefault('HIVE_MIND_SENTRY_PROFILE_SESSION_SAMPLE_RATE_DEV', 1.0),
|
|
108
|
+
profileSessionSampleRateProd: parseFloatWithDefault('HIVE_MIND_SENTRY_PROFILE_SESSION_SAMPLE_RATE_PROD', 0.1),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// External URLs
|
|
112
|
+
export const externalUrls = {
|
|
113
|
+
githubBase: getenv('HIVE_MIND_GITHUB_BASE_URL', 'https://github.com'),
|
|
114
|
+
bunInstall: getenv('HIVE_MIND_BUN_INSTALL_URL', 'https://bun.sh/'),
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Model configurations
|
|
118
|
+
// Default available models in Links Notation format (only aliases)
|
|
119
|
+
const defaultAvailableModels = `(
|
|
120
|
+
opus
|
|
121
|
+
sonnet
|
|
122
|
+
haiku
|
|
123
|
+
)`;
|
|
124
|
+
|
|
125
|
+
export const modelConfig = {
|
|
126
|
+
availableModels: (() => {
|
|
127
|
+
const envValue = getenv('HIVE_MIND_AVAILABLE_MODELS', defaultAvailableModels);
|
|
128
|
+
// Parse Links Notation format
|
|
129
|
+
const parsed = lino.parse(envValue);
|
|
130
|
+
// If parsing returns empty array, fall back to the three aliases
|
|
131
|
+
return parsed.length > 0 ? parsed : ['opus', 'sonnet', 'haiku'];
|
|
132
|
+
})(),
|
|
133
|
+
defaultModel: getenv('HIVE_MIND_DEFAULT_MODEL', 'sonnet'),
|
|
134
|
+
// Allow any model ID - validation is delegated to the tool implementation
|
|
135
|
+
restrictModels: getenv('HIVE_MIND_RESTRICT_MODELS', 'false').toLowerCase() === 'true',
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// Version configurations
|
|
139
|
+
export const version = {
|
|
140
|
+
fallback: getenv('HIVE_MIND_VERSION_FALLBACK', '0.14.3'),
|
|
141
|
+
default: getenv('HIVE_MIND_VERSION_DEFAULT', '0.14.3'),
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Helper function to validate configuration values
|
|
145
|
+
export function validateConfig() {
|
|
146
|
+
// Ensure all numeric values are valid
|
|
147
|
+
const numericConfigs = [
|
|
148
|
+
...Object.values(timeouts),
|
|
149
|
+
...Object.values(githubLimits),
|
|
150
|
+
...Object.values(systemLimits),
|
|
151
|
+
...Object.values(retryLimits).filter(v => typeof v === 'number'),
|
|
152
|
+
...Object.values(textProcessing),
|
|
153
|
+
display.labelWidth,
|
|
154
|
+
autoContinue.ageThresholdHours,
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
for (const value of numericConfigs) {
|
|
158
|
+
if (isNaN(value) || value < 0) {
|
|
159
|
+
throw new Error(`Invalid numeric configuration value: ${value}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Ensure sample rates are between 0 and 1
|
|
164
|
+
const sampleRates = [
|
|
165
|
+
sentry.tracesSampleRateDev,
|
|
166
|
+
sentry.tracesSampleRateProd,
|
|
167
|
+
sentry.profileSessionSampleRateDev,
|
|
168
|
+
sentry.profileSessionSampleRateProd,
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
for (const rate of sampleRates) {
|
|
172
|
+
if (isNaN(rate) || rate < 0 || rate > 1) {
|
|
173
|
+
throw new Error(`Invalid sample rate configuration: ${rate}. Must be between 0 and 1.`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Ensure required paths exist
|
|
178
|
+
if (!filePaths.tempDir) {
|
|
179
|
+
throw new Error('tempDir configuration is required');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Export a function to get all configurations as an object (useful for debugging)
|
|
186
|
+
export function getAllConfigurations() {
|
|
187
|
+
return {
|
|
188
|
+
timeouts,
|
|
189
|
+
autoContinue,
|
|
190
|
+
githubLimits,
|
|
191
|
+
systemLimits,
|
|
192
|
+
retryLimits,
|
|
193
|
+
filePaths,
|
|
194
|
+
textProcessing,
|
|
195
|
+
display,
|
|
196
|
+
sentry,
|
|
197
|
+
externalUrls,
|
|
198
|
+
modelConfig,
|
|
199
|
+
version,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Export a function to print current configuration (useful for debugging)
|
|
204
|
+
export function printConfiguration() {
|
|
205
|
+
console.log('Current Configuration:');
|
|
206
|
+
console.log(JSON.stringify(getAllConfigurations(), null, 2));
|
|
207
|
+
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Contributing Guidelines Detection and Fetching
|
|
5
|
+
* Handles finding and fetching contributing guidelines from repositories
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
if (typeof globalThis.use === 'undefined') {
|
|
9
|
+
globalThis.use = (await eval(await (await fetch('https://unpkg.com/use-m/use.js')).text())).use;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { $ } = await use('command-stream');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Common paths where contributing guidelines might be found
|
|
16
|
+
*/
|
|
17
|
+
const CONTRIBUTING_PATHS = [
|
|
18
|
+
'CONTRIBUTING.md',
|
|
19
|
+
'CONTRIBUTING',
|
|
20
|
+
'docs/CONTRIBUTING.md',
|
|
21
|
+
'docs/contributing.md',
|
|
22
|
+
'.github/CONTRIBUTING.md',
|
|
23
|
+
'CONTRIBUTE.md',
|
|
24
|
+
'docs/contribute.md'
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Common documentation URLs patterns
|
|
29
|
+
*/
|
|
30
|
+
const DOCS_PATTERNS = [
|
|
31
|
+
'readthedocs.io',
|
|
32
|
+
'github.io',
|
|
33
|
+
'/docs/',
|
|
34
|
+
'/documentation/'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Detect contributing guidelines in a repository
|
|
39
|
+
* @param {string} owner - Repository owner
|
|
40
|
+
* @param {string} repo - Repository name
|
|
41
|
+
* @returns {Promise<Object>} Object with guidelines info {found: boolean, path: string, url: string, content: string}
|
|
42
|
+
*/
|
|
43
|
+
export async function detectContributingGuidelines(owner, repo) {
|
|
44
|
+
const result = {
|
|
45
|
+
found: false,
|
|
46
|
+
path: null,
|
|
47
|
+
url: null,
|
|
48
|
+
content: null,
|
|
49
|
+
docsUrl: null
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Try to find CONTRIBUTING file in the repo
|
|
53
|
+
for (const path of CONTRIBUTING_PATHS) {
|
|
54
|
+
try {
|
|
55
|
+
const checkResult = await $`gh api repos/${owner}/${repo}/contents/${path} 2>/dev/null`.raw().trim();
|
|
56
|
+
if (checkResult.exitCode === 0 && checkResult.text) {
|
|
57
|
+
result.found = true;
|
|
58
|
+
result.path = path;
|
|
59
|
+
result.url = `https://github.com/${owner}/${repo}/blob/main/${path}`;
|
|
60
|
+
|
|
61
|
+
// Try to get the content from the response
|
|
62
|
+
try {
|
|
63
|
+
const data = JSON.parse(checkResult.text);
|
|
64
|
+
if (data.content) {
|
|
65
|
+
// Decode base64 content
|
|
66
|
+
result.content = Buffer.from(data.content, 'base64').toString('utf-8');
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// Content parse failed, but we know the file exists
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// File doesn't exist, try next path
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Try to find docs URL in README
|
|
80
|
+
if (!result.found) {
|
|
81
|
+
try {
|
|
82
|
+
const readme = await $`gh api repos/${owner}/${repo}/readme 2>/dev/null`.raw().trim();
|
|
83
|
+
if (readme.exitCode === 0 && readme.text) {
|
|
84
|
+
const readmeData = JSON.parse(readme.text);
|
|
85
|
+
const readmeContent = Buffer.from(readmeData.content, 'base64').toString('utf-8');
|
|
86
|
+
|
|
87
|
+
// Look for contributing documentation URL
|
|
88
|
+
const contributingMatch = readmeContent.match(/https?:\/\/[^\s)]+contributing[^\s)]*/gi);
|
|
89
|
+
if (contributingMatch && contributingMatch[0]) {
|
|
90
|
+
result.found = true;
|
|
91
|
+
result.docsUrl = contributingMatch[0];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Look for general docs URL that might contain contributing info
|
|
95
|
+
if (!result.found) {
|
|
96
|
+
for (const pattern of DOCS_PATTERNS) {
|
|
97
|
+
const docsMatch = readmeContent.match(new RegExp(`https?:\\/\\/[^\\s\\)]*${pattern}[^\\s\\)]*`, 'gi'));
|
|
98
|
+
if (docsMatch && docsMatch[0]) {
|
|
99
|
+
result.found = true;
|
|
100
|
+
result.docsUrl = docsMatch[0];
|
|
101
|
+
// Try to construct a contributing URL from the docs URL
|
|
102
|
+
const baseUrl = docsMatch[0].replace(/\/$/, '');
|
|
103
|
+
result.url = `${baseUrl}/contributing.html`;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} catch {
|
|
110
|
+
// README fetch failed
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Extract CI/CD requirements from contributing guidelines
|
|
119
|
+
* @param {string} content - Contributing guidelines content
|
|
120
|
+
* @returns {Object} Extracted requirements {linters: [], testCommands: [], styleGuide: []}
|
|
121
|
+
*/
|
|
122
|
+
export function extractCIRequirements(content) {
|
|
123
|
+
const requirements = {
|
|
124
|
+
linters: [],
|
|
125
|
+
testCommands: [],
|
|
126
|
+
styleGuide: [],
|
|
127
|
+
preCommitChecks: []
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
if (!content) return requirements;
|
|
131
|
+
|
|
132
|
+
const lowerContent = content.toLowerCase();
|
|
133
|
+
|
|
134
|
+
// Detect linters
|
|
135
|
+
if (lowerContent.includes('ruff')) {
|
|
136
|
+
requirements.linters.push({ name: 'ruff', command: 'ruff check .' });
|
|
137
|
+
}
|
|
138
|
+
if (lowerContent.includes('eslint')) {
|
|
139
|
+
requirements.linters.push({ name: 'eslint', command: 'npm run lint' });
|
|
140
|
+
}
|
|
141
|
+
if (lowerContent.includes('pylint')) {
|
|
142
|
+
requirements.linters.push({ name: 'pylint', command: 'pylint .' });
|
|
143
|
+
}
|
|
144
|
+
if (lowerContent.includes('flake8')) {
|
|
145
|
+
requirements.linters.push({ name: 'flake8', command: 'flake8 .' });
|
|
146
|
+
}
|
|
147
|
+
if (lowerContent.includes('mypy')) {
|
|
148
|
+
requirements.linters.push({ name: 'mypy', command: 'mypy .' });
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Detect test commands
|
|
152
|
+
if (lowerContent.includes('pytest')) {
|
|
153
|
+
requirements.testCommands.push({ name: 'pytest', command: 'pytest' });
|
|
154
|
+
}
|
|
155
|
+
if (lowerContent.includes('nox')) {
|
|
156
|
+
requirements.testCommands.push({ name: 'nox', command: 'nox' });
|
|
157
|
+
}
|
|
158
|
+
if (lowerContent.includes('npm test')) {
|
|
159
|
+
requirements.testCommands.push({ name: 'npm test', command: 'npm test' });
|
|
160
|
+
}
|
|
161
|
+
if (lowerContent.includes('cargo test')) {
|
|
162
|
+
requirements.testCommands.push({ name: 'cargo test', command: 'cargo test' });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Extract line length requirements
|
|
166
|
+
const lineLengthMatch = content.match(/(\d+)\s+character[s]?\s+line\s+limit/i);
|
|
167
|
+
if (lineLengthMatch) {
|
|
168
|
+
requirements.styleGuide.push(`Maximum line length: ${lineLengthMatch[1]} characters`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Extract pre-commit requirements
|
|
172
|
+
if (lowerContent.includes('pre-commit')) {
|
|
173
|
+
requirements.preCommitChecks.push('pre-commit hooks are required');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return requirements;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Build contributing guidelines section for CLAUDE.md
|
|
181
|
+
* @param {string} owner - Repository owner
|
|
182
|
+
* @param {string} repo - Repository name
|
|
183
|
+
* @returns {Promise<string>} Formatted contributing guidelines section
|
|
184
|
+
*/
|
|
185
|
+
export async function buildContributingSection(owner, repo) {
|
|
186
|
+
const guidelines = await detectContributingGuidelines(owner, repo);
|
|
187
|
+
|
|
188
|
+
if (!guidelines.found) {
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const lines = [];
|
|
193
|
+
lines.push('');
|
|
194
|
+
lines.push('## Contributing Guidelines');
|
|
195
|
+
|
|
196
|
+
if (guidelines.url) {
|
|
197
|
+
lines.push(`📋 Contributing Guide: ${guidelines.url}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (guidelines.docsUrl) {
|
|
201
|
+
lines.push(`📚 Documentation: ${guidelines.docsUrl}`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Extract and display CI requirements
|
|
205
|
+
if (guidelines.content) {
|
|
206
|
+
const requirements = extractCIRequirements(guidelines.content);
|
|
207
|
+
|
|
208
|
+
if (requirements.linters.length > 0 || requirements.testCommands.length > 0) {
|
|
209
|
+
lines.push('');
|
|
210
|
+
lines.push('### Required Checks Before Committing:');
|
|
211
|
+
|
|
212
|
+
if (requirements.linters.length > 0) {
|
|
213
|
+
lines.push('');
|
|
214
|
+
lines.push('**Linting:**');
|
|
215
|
+
requirements.linters.forEach(linter => {
|
|
216
|
+
lines.push(`- \`${linter.command}\``);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (requirements.testCommands.length > 0) {
|
|
221
|
+
lines.push('');
|
|
222
|
+
lines.push('**Testing:**');
|
|
223
|
+
requirements.testCommands.forEach(test => {
|
|
224
|
+
lines.push(`- \`${test.command}\``);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (requirements.styleGuide.length > 0) {
|
|
229
|
+
lines.push('');
|
|
230
|
+
lines.push('**Style Guide:**');
|
|
231
|
+
requirements.styleGuide.forEach(rule => {
|
|
232
|
+
lines.push(`- ${rule}`);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
lines.push('');
|
|
239
|
+
return lines.join('\n');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Check for workflow approval requirements in GitHub Actions
|
|
244
|
+
* @param {string} owner - Repository owner
|
|
245
|
+
* @param {string} repo - Repository name
|
|
246
|
+
* @returns {Promise<Object>} Workflow status info
|
|
247
|
+
*/
|
|
248
|
+
export async function checkWorkflowApprovalStatus(owner, repo) {
|
|
249
|
+
try {
|
|
250
|
+
// Get workflow runs for the PR
|
|
251
|
+
const runsResult = await $`gh run list --repo ${owner}/${repo} --json databaseId,status,conclusion,event --limit 5`.trim();
|
|
252
|
+
|
|
253
|
+
if (runsResult.exitCode !== 0) {
|
|
254
|
+
return { hasApprovalRequired: false, runs: [] };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const runs = JSON.parse(runsResult.text);
|
|
258
|
+
const approvalRequiredRuns = runs.filter(run => run.conclusion === 'action_required');
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
hasApprovalRequired: approvalRequiredRuns.length > 0,
|
|
262
|
+
runs: approvalRequiredRuns,
|
|
263
|
+
totalRuns: runs.length
|
|
264
|
+
};
|
|
265
|
+
} catch (err) {
|
|
266
|
+
return { hasApprovalRequired: false, runs: [], error: err.message };
|
|
267
|
+
}
|
|
268
|
+
}
|