@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.28.0",
3
+ "version": "1.30.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",
@@ -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
+ }
@@ -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;