@link-assistant/hive-mind 1.28.0 â 1.30.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 +28 -0
- package/package.json +1 -1
- package/src/solve.accept-invite.lib.mjs +89 -0
- package/src/solve.config.lib.mjs +5 -0
- package/src/solve.mjs +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.30.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ee6233a: Optimize Docker build by using pinned konard/sandbox version as base image
|
|
8
|
+
- Docker image now inherits from `konard/sandbox:1.3.16` (pinned) instead of building from scratch
|
|
9
|
+
- Significantly faster build times (2-3 min vs 10-15+ min) as general-purpose tools are pre-installed
|
|
10
|
+
- Reduced timeout risk since heavy installations (Homebrew, PHP, etc.) are handled by base image
|
|
11
|
+
- Removed `scripts/ubuntu-24-server-install.sh` (functionality now provided by sandbox)
|
|
12
|
+
- User renamed from `sandbox` to `hive` for backward compatibility
|
|
13
|
+
- Sandbox version is pinned to `1.3.16` for stable, reproducible builds (instead of `latest`)
|
|
14
|
+
- Docker image is versioned to match the published npm package version
|
|
15
|
+
- Docker builds are triggered only after npm package availability is confirmed
|
|
16
|
+
|
|
17
|
+
This change implements the separation of concerns described in link-foundation/sandbox#65:
|
|
18
|
+
- sandbox: Universal development environment with all general-purpose tools
|
|
19
|
+
- hive-mind: AI-specific tools (Claude CLI, Playwright MCP, etc.) built on top of sandbox
|
|
20
|
+
|
|
21
|
+
## 1.29.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- 161b595: feat: add --auto-accept-invite option to solve command
|
|
26
|
+
|
|
27
|
+
Adds a new `--auto-accept-invite` boolean option to the `solve` command that automatically accepts the pending GitHub repository or organization invitation for the specific repository/organization being solved, before checking write access.
|
|
28
|
+
|
|
29
|
+
Unlike the `/accept_invites` Telegram command (which accepts ALL pending invitations), this option is scoped to the target repo/org only, making it safer and more targeted. Useful when you've just been invited to a repository and want to run `solve` without manually accepting the invitation first.
|
|
30
|
+
|
|
3
31
|
## 1.28.0
|
|
4
32
|
|
|
5
33
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-accept GitHub invitation for a specific repository/organization.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the /accept_invites Telegram command (which accepts ALL pending invitations),
|
|
5
|
+
* this module only accepts the invitation for the specific repository or organization
|
|
6
|
+
* that is being solved. This is safer and more targeted.
|
|
7
|
+
*
|
|
8
|
+
* @see https://docs.github.com/en/rest/collaborators/invitations
|
|
9
|
+
* @see https://docs.github.com/en/rest/orgs/members
|
|
10
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1373
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { promisify } from 'util';
|
|
14
|
+
import { exec as execCallback } from 'child_process';
|
|
15
|
+
|
|
16
|
+
const exec = promisify(execCallback);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Accepts pending GitHub repository or organization invitation for a specific target.
|
|
20
|
+
*
|
|
21
|
+
* Checks for a pending repository invitation matching `owner/repo` and/or a pending
|
|
22
|
+
* organization membership for `owner`. Accepts only the matching invitation(s).
|
|
23
|
+
*
|
|
24
|
+
* @param {string} owner - Repository owner (user or organization login)
|
|
25
|
+
* @param {string} repo - Repository name
|
|
26
|
+
* @param {Function} log - Logging function
|
|
27
|
+
* @param {boolean} verbose - Whether verbose logging is enabled
|
|
28
|
+
* @returns {Promise<{acceptedRepo: boolean, acceptedOrg: boolean}>} Result of acceptance attempts
|
|
29
|
+
*/
|
|
30
|
+
export async function autoAcceptInviteForRepo(owner, repo, log, verbose) {
|
|
31
|
+
const result = { acceptedRepo: false, acceptedOrg: false };
|
|
32
|
+
const fullName = `${owner}/${repo}`;
|
|
33
|
+
|
|
34
|
+
await log(`đ --auto-accept-invite: Checking for pending invitation to ${fullName}...`);
|
|
35
|
+
|
|
36
|
+
// Check for pending repository invitation
|
|
37
|
+
try {
|
|
38
|
+
const { stdout: repoInvJson } = await exec('gh api /user/repository_invitations 2>/dev/null || echo "[]"');
|
|
39
|
+
const repoInvitations = JSON.parse(repoInvJson.trim() || '[]');
|
|
40
|
+
verbose && (await log(` Found ${repoInvitations.length} total pending repo invitation(s)`, { verbose: true }));
|
|
41
|
+
|
|
42
|
+
const matchingInv = repoInvitations.find(inv => inv.repository?.full_name?.toLowerCase() === fullName.toLowerCase());
|
|
43
|
+
|
|
44
|
+
if (matchingInv) {
|
|
45
|
+
try {
|
|
46
|
+
await exec(`gh api -X PATCH /user/repository_invitations/${matchingInv.id}`);
|
|
47
|
+
await log(`â
--auto-accept-invite: Accepted repository invitation for ${fullName}`);
|
|
48
|
+
result.acceptedRepo = true;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
await log(`â ī¸ --auto-accept-invite: Failed to accept repository invitation for ${fullName}: ${e.message}`, { level: 'warning' });
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
verbose && (await log(` No pending repository invitation found for ${fullName}`, { verbose: true }));
|
|
54
|
+
}
|
|
55
|
+
} catch (e) {
|
|
56
|
+
verbose && (await log(` Could not fetch repository invitations: ${e.message}`, { verbose: true }));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for pending organization membership
|
|
60
|
+
try {
|
|
61
|
+
const { stdout: orgMemJson } = await exec('gh api /user/memberships/orgs 2>/dev/null || echo "[]"');
|
|
62
|
+
const orgMemberships = JSON.parse(orgMemJson.trim() || '[]');
|
|
63
|
+
const pendingOrgs = orgMemberships.filter(m => m.state === 'pending');
|
|
64
|
+
verbose && (await log(` Found ${pendingOrgs.length} total pending org invitation(s)`, { verbose: true }));
|
|
65
|
+
|
|
66
|
+
const matchingOrg = pendingOrgs.find(m => m.organization?.login?.toLowerCase() === owner.toLowerCase());
|
|
67
|
+
|
|
68
|
+
if (matchingOrg) {
|
|
69
|
+
const orgName = matchingOrg.organization.login;
|
|
70
|
+
try {
|
|
71
|
+
await exec(`gh api -X PATCH /user/memberships/orgs/${orgName} -f state=active`);
|
|
72
|
+
await log(`â
--auto-accept-invite: Accepted organization invitation for ${orgName}`);
|
|
73
|
+
result.acceptedOrg = true;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
await log(`â ī¸ --auto-accept-invite: Failed to accept organization invitation for ${orgName}: ${e.message}`, { level: 'warning' });
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
verbose && (await log(` No pending organization invitation found for ${owner}`, { verbose: true }));
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
verbose && (await log(` Could not fetch organization memberships: ${e.message}`, { verbose: true }));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!result.acceptedRepo && !result.acceptedOrg) {
|
|
85
|
+
await log(`âšī¸ --auto-accept-invite: No pending invitation found for ${fullName} or organization ${owner}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
|
+
}
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -369,6 +369,11 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
369
369
|
description: 'Automatically attach solution summary only if the AI did not create any comments during the session. This provides visible feedback when the AI completes silently.',
|
|
370
370
|
default: false,
|
|
371
371
|
},
|
|
372
|
+
'auto-accept-invite': {
|
|
373
|
+
type: 'boolean',
|
|
374
|
+
description: 'Automatically accept the pending GitHub repository or organization invitation for the specific repository/organization being solved, before checking write access. Unlike /accept_invites which accepts all pending invitations, this only accepts the invite for the target repo/org.',
|
|
375
|
+
default: false,
|
|
376
|
+
},
|
|
372
377
|
};
|
|
373
378
|
|
|
374
379
|
// Function to create yargs configuration - avoids duplication
|
package/src/solve.mjs
CHANGED
|
@@ -94,6 +94,8 @@ const { prepareFeedbackAndTimestamps, checkUncommittedChanges, checkForkActions
|
|
|
94
94
|
// Import model validation library
|
|
95
95
|
const modelValidation = await import('./model-validation.lib.mjs');
|
|
96
96
|
const { validateAndExitOnInvalidModel } = modelValidation;
|
|
97
|
+
const acceptInviteLib = await import('./solve.accept-invite.lib.mjs');
|
|
98
|
+
const { autoAcceptInviteForRepo } = acceptInviteLib;
|
|
97
99
|
|
|
98
100
|
// Initialize log file EARLY to capture all output including version and command
|
|
99
101
|
// Use default directory (cwd) initially, will be set from argv.logDir after parsing
|
|
@@ -313,6 +315,11 @@ if (argv.autoFork && !argv.fork) {
|
|
|
313
315
|
}
|
|
314
316
|
}
|
|
315
317
|
|
|
318
|
+
// Accept pending GitHub invitation for the specific repo/org before checking write access
|
|
319
|
+
if (argv.autoAcceptInvite) {
|
|
320
|
+
await autoAcceptInviteForRepo(owner, repo, log, argv.verbose);
|
|
321
|
+
}
|
|
322
|
+
|
|
316
323
|
// Early check: Verify repository write permissions BEFORE doing any work
|
|
317
324
|
// This prevents wasting AI tokens when user doesn't have access and --fork is not used
|
|
318
325
|
const { checkRepositoryWritePermission } = githubLib;
|