@agentuity/cli 1.0.13 → 1.0.15

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.
Files changed (135) hide show
  1. package/bin/cli.ts +8 -0
  2. package/dist/agent-detection.js +2 -2
  3. package/dist/agent-detection.js.map +1 -1
  4. package/dist/banner.js +2 -2
  5. package/dist/banner.js.map +1 -1
  6. package/dist/bun-path.d.ts.map +1 -1
  7. package/dist/bun-path.js +2 -1
  8. package/dist/bun-path.js.map +1 -1
  9. package/dist/cmd/build/ast.d.ts.map +1 -1
  10. package/dist/cmd/build/ast.js +87 -14
  11. package/dist/cmd/build/ast.js.map +1 -1
  12. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  13. package/dist/cmd/build/vite/agent-discovery.js +3 -2
  14. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  15. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  16. package/dist/cmd/build/vite/metadata-generator.js +2 -1
  17. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  18. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  19. package/dist/cmd/build/vite/registry-generator.js +9 -7
  20. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  21. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  22. package/dist/cmd/build/vite/route-discovery.js +3 -2
  23. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  24. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  25. package/dist/cmd/cloud/deploy.js +19 -21
  26. package/dist/cmd/cloud/deploy.js.map +1 -1
  27. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  28. package/dist/cmd/cloud/env/delete.js +3 -26
  29. package/dist/cmd/cloud/env/delete.js.map +1 -1
  30. package/dist/cmd/cloud/env/import.d.ts.map +1 -1
  31. package/dist/cmd/cloud/env/import.js +3 -16
  32. package/dist/cmd/cloud/env/import.js.map +1 -1
  33. package/dist/cmd/cloud/env/set.d.ts.map +1 -1
  34. package/dist/cmd/cloud/env/set.js +3 -19
  35. package/dist/cmd/cloud/env/set.js.map +1 -1
  36. package/dist/cmd/cloud/sandbox/cp.d.ts.map +1 -1
  37. package/dist/cmd/cloud/sandbox/cp.js +2 -1
  38. package/dist/cmd/cloud/sandbox/cp.js.map +1 -1
  39. package/dist/cmd/git/account/add.d.ts +6 -8
  40. package/dist/cmd/git/account/add.d.ts.map +1 -1
  41. package/dist/cmd/git/account/add.js +37 -151
  42. package/dist/cmd/git/account/add.js.map +1 -1
  43. package/dist/cmd/git/account/index.d.ts +0 -1
  44. package/dist/cmd/git/account/index.d.ts.map +1 -1
  45. package/dist/cmd/git/account/index.js +3 -4
  46. package/dist/cmd/git/account/index.js.map +1 -1
  47. package/dist/cmd/git/account/list.d.ts.map +1 -1
  48. package/dist/cmd/git/account/list.js +35 -67
  49. package/dist/cmd/git/account/list.js.map +1 -1
  50. package/dist/cmd/git/account/remove.d.ts.map +1 -1
  51. package/dist/cmd/git/account/remove.js +42 -84
  52. package/dist/cmd/git/account/remove.js.map +1 -1
  53. package/dist/cmd/git/api.d.ts +19 -23
  54. package/dist/cmd/git/api.d.ts.map +1 -1
  55. package/dist/cmd/git/api.js +38 -56
  56. package/dist/cmd/git/api.js.map +1 -1
  57. package/dist/cmd/git/identity/connect.d.ts +15 -0
  58. package/dist/cmd/git/identity/connect.d.ts.map +1 -0
  59. package/dist/cmd/git/identity/connect.js +135 -0
  60. package/dist/cmd/git/identity/connect.js.map +1 -0
  61. package/dist/cmd/git/identity/disconnect.d.ts +2 -0
  62. package/dist/cmd/git/identity/disconnect.d.ts.map +1 -0
  63. package/dist/cmd/git/identity/disconnect.js +83 -0
  64. package/dist/cmd/git/identity/disconnect.js.map +1 -0
  65. package/dist/cmd/git/identity/index.d.ts +2 -0
  66. package/dist/cmd/git/identity/index.d.ts.map +1 -0
  67. package/dist/cmd/git/identity/index.js +10 -0
  68. package/dist/cmd/git/identity/index.js.map +1 -0
  69. package/dist/cmd/git/identity/status.d.ts +2 -0
  70. package/dist/cmd/git/identity/status.d.ts.map +1 -0
  71. package/dist/cmd/git/identity/status.js +77 -0
  72. package/dist/cmd/git/identity/status.js.map +1 -0
  73. package/dist/cmd/git/index.d.ts +0 -1
  74. package/dist/cmd/git/index.d.ts.map +1 -1
  75. package/dist/cmd/git/index.js +3 -2
  76. package/dist/cmd/git/index.js.map +1 -1
  77. package/dist/cmd/git/link.d.ts +2 -3
  78. package/dist/cmd/git/link.d.ts.map +1 -1
  79. package/dist/cmd/git/link.js +22 -28
  80. package/dist/cmd/git/link.js.map +1 -1
  81. package/dist/cmd/git/list.d.ts.map +1 -1
  82. package/dist/cmd/git/list.js +42 -55
  83. package/dist/cmd/git/list.js.map +1 -1
  84. package/dist/cmd/git/status.d.ts.map +1 -1
  85. package/dist/cmd/git/status.js +51 -38
  86. package/dist/cmd/git/status.js.map +1 -1
  87. package/dist/cmd/upgrade/npm-availability.d.ts +6 -7
  88. package/dist/cmd/upgrade/npm-availability.d.ts.map +1 -1
  89. package/dist/cmd/upgrade/npm-availability.js +9 -18
  90. package/dist/cmd/upgrade/npm-availability.js.map +1 -1
  91. package/dist/config.d.ts.map +1 -1
  92. package/dist/config.js +14 -4
  93. package/dist/config.js.map +1 -1
  94. package/dist/utils/detectSubagent.d.ts.map +1 -1
  95. package/dist/utils/detectSubagent.js +3 -0
  96. package/dist/utils/detectSubagent.js.map +1 -1
  97. package/dist/utils/normalize-path.d.ts +11 -0
  98. package/dist/utils/normalize-path.d.ts.map +1 -0
  99. package/dist/utils/normalize-path.js +13 -0
  100. package/dist/utils/normalize-path.js.map +1 -0
  101. package/dist/utils/zip.d.ts.map +1 -1
  102. package/dist/utils/zip.js +2 -1
  103. package/dist/utils/zip.js.map +1 -1
  104. package/package.json +6 -6
  105. package/src/agent-detection.ts +2 -2
  106. package/src/banner.ts +2 -2
  107. package/src/bun-path.ts +2 -1
  108. package/src/cmd/build/ast.ts +96 -15
  109. package/src/cmd/build/vite/agent-discovery.ts +3 -2
  110. package/src/cmd/build/vite/metadata-generator.ts +2 -1
  111. package/src/cmd/build/vite/registry-generator.ts +9 -7
  112. package/src/cmd/build/vite/route-discovery.ts +3 -2
  113. package/src/cmd/cloud/deploy.ts +54 -61
  114. package/src/cmd/cloud/env/delete.ts +3 -34
  115. package/src/cmd/cloud/env/import.ts +2 -18
  116. package/src/cmd/cloud/env/set.ts +2 -21
  117. package/src/cmd/cloud/sandbox/cp.ts +2 -1
  118. package/src/cmd/git/account/add.ts +51 -190
  119. package/src/cmd/git/account/index.ts +3 -5
  120. package/src/cmd/git/account/list.ts +51 -82
  121. package/src/cmd/git/account/remove.ts +45 -95
  122. package/src/cmd/git/api.ts +49 -111
  123. package/src/cmd/git/identity/connect.ts +178 -0
  124. package/src/cmd/git/identity/disconnect.ts +103 -0
  125. package/src/cmd/git/identity/index.ts +10 -0
  126. package/src/cmd/git/identity/status.ts +96 -0
  127. package/src/cmd/git/index.ts +3 -3
  128. package/src/cmd/git/link.ts +32 -35
  129. package/src/cmd/git/list.ts +48 -59
  130. package/src/cmd/git/status.ts +55 -40
  131. package/src/cmd/upgrade/npm-availability.ts +14 -23
  132. package/src/config.ts +14 -5
  133. package/src/utils/detectSubagent.ts +5 -0
  134. package/src/utils/normalize-path.ts +12 -0
  135. package/src/utils/zip.ts +2 -1
@@ -3,8 +3,6 @@ import { createSubcommand } from '../../../types';
3
3
  import * as tui from '../../../tui';
4
4
  import { projectEnvUpdate, orgEnvUpdate } from '@agentuity/server';
5
5
  import {
6
- findExistingEnvFile,
7
- writeEnvFile,
8
6
  looksLikeSecret,
9
7
  isReservedAgentuityKey,
10
8
  isPublicVarKey,
@@ -75,10 +73,6 @@ function parseEnvArgs(rawArgs: string[]): ParsedEnvPair[] {
75
73
  const EnvSetResponseSchema = z.object({
76
74
  success: z.boolean().describe('Whether the operation succeeded'),
77
75
  keys: z.array(z.string()).describe('Environment variable keys that were set'),
78
- path: z
79
- .string()
80
- .optional()
81
- .describe('Local file path where env vars were saved (project scope only)'),
82
76
  secretKeys: z.array(z.string()).describe('Keys that were stored as secrets'),
83
77
  envKeys: z.array(z.string()).describe('Keys that were stored as env vars'),
84
78
  scope: z.enum(['project', 'org']).describe('The scope where the variables were set'),
@@ -134,7 +128,7 @@ export const setSubcommand = createSubcommand({
134
128
  },
135
129
 
136
130
  async handler(ctx) {
137
- const { args: cmdArgs, opts, apiClient, project, projectDir, config } = ctx;
131
+ const { args: cmdArgs, opts, apiClient, project, config } = ctx;
138
132
  const useOrgScope = isOrgScope(opts?.org);
139
133
  const forceSecret = opts?.secret ?? false;
140
134
 
@@ -262,26 +256,13 @@ export const setSubcommand = createSubcommand({
262
256
  }
263
257
  );
264
258
 
265
- // Update local .env file only if we have a project directory
266
- let envFilePath: string | undefined;
267
- if (projectDir) {
268
- envFilePath = await findExistingEnvFile(projectDir);
269
- const allPairsForLocal: Record<string, string> = {
270
- ...envPairs,
271
- ...secretPairs,
272
- };
273
- await writeEnvFile(envFilePath, allPairsForLocal);
274
- }
275
-
276
- const locationMsg = envFilePath ? ` (cloud + ${envFilePath})` : ' (cloud only)';
277
259
  tui.success(
278
- `Variable${totalCount !== 1 ? 's' : ''} set successfully: ${allKeys.join(', ')}${secretSuffix}${locationMsg}`
260
+ `Variable${totalCount !== 1 ? 's' : ''} set successfully: ${allKeys.join(', ')}${secretSuffix}`
279
261
  );
280
262
 
281
263
  return {
282
264
  success: true,
283
265
  keys: allKeys,
284
- path: envFilePath,
285
266
  secretKeys: secretKeysList,
286
267
  envKeys: envKeysList,
287
268
  scope: 'project' as const,
@@ -2,6 +2,7 @@ import { z } from 'zod';
2
2
  import { readFileSync, writeFileSync, mkdirSync, statSync, readdirSync } from 'node:fs';
3
3
  import { dirname, resolve, basename, join, relative } from 'node:path';
4
4
  import { createCommand } from '../../../types';
5
+ import { toForwardSlash } from '../../../utils/normalize-path';
5
6
  import * as tui from '../../../tui';
6
7
  import { createSandboxClient } from './util';
7
8
  import { getCommand } from '../../../command-prefix';
@@ -265,7 +266,7 @@ async function uploadDirectory(
265
266
  : effectiveRemotePath;
266
267
 
267
268
  for (const filePath of allFiles) {
268
- const relativePath = relative(localDir, filePath);
269
+ const relativePath = toForwardSlash(relative(localDir, filePath));
269
270
  const targetPath = `${baseRemotePath}/${relativePath}`;
270
271
  const buffer = readFileSync(filePath);
271
272
  files.push({ path: targetPath, content: buffer });
@@ -1,119 +1,62 @@
1
- import { createSubcommand } from '../../../types';
2
- import type { Config } from '../../../types';
3
- import * as tui from '../../../tui';
4
- import { getCommand } from '../../../command-prefix';
5
- import { getAPIBaseURL } from '../../../api';
6
- import type { APIClient } from '../../../api';
7
- import { ErrorCode } from '../../../errors';
8
- import { listOrganizations } from '@agentuity/server';
9
- import enquirer from 'enquirer';
10
1
  import type { Logger } from '@agentuity/core';
11
2
  import { z } from 'zod';
3
+ import type { APIClient } from '../../../api';
4
+ import { getAPIBaseURL } from '../../../api';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { ErrorCode } from '../../../errors';
7
+ import * as tui from '../../../tui';
8
+ import type { Config } from '../../../types';
9
+ import { createSubcommand } from '../../../types';
12
10
  import {
13
- startGithubIntegration,
14
- pollForGithubIntegration,
15
11
  getGithubIntegrationStatus,
16
- getExistingGithubIntegrations,
17
- copyGithubIntegration,
12
+ pollForGithubIntegration,
13
+ startGithubIntegration,
18
14
  } from '../api';
19
15
 
20
- export interface RunGitAccountConnectOptions {
16
+ export interface RunGitAccountAddOptions {
21
17
  apiClient: APIClient;
22
- orgId: string;
23
- orgName?: string;
24
18
  logger: Logger;
25
19
  config?: Config | null;
26
20
  }
27
21
 
28
- export interface RunGitAccountConnectResult {
29
- connected: boolean;
22
+ export interface RunGitAccountAddResult {
23
+ added: boolean;
30
24
  cancelled?: boolean;
31
25
  }
32
26
 
33
- export async function runGitAccountConnect(
34
- options: RunGitAccountConnectOptions
35
- ): Promise<RunGitAccountConnectResult> {
36
- const { apiClient, orgId, orgName, logger, config } = options;
37
- const orgDisplay = orgName ?? orgId;
27
+ export async function runGitAccountAdd(
28
+ options: RunGitAccountAddOptions
29
+ ): Promise<RunGitAccountAddResult> {
30
+ const { apiClient, logger, config } = options;
38
31
 
39
32
  try {
40
- const currentStatus = await getGithubIntegrationStatus(apiClient, orgId);
41
- const initialCount = currentStatus.integrations?.length ?? 0;
42
-
43
- const existingIntegrations = await tui.spinner({
44
- message: 'Checking for existing GitHub connections...',
45
- clearOnSuccess: true,
46
- callback: () => getExistingGithubIntegrations(apiClient, orgId),
47
- });
33
+ const currentStatus = await getGithubIntegrationStatus(apiClient);
48
34
 
49
- const alreadyConnectedNames = new Set(
50
- currentStatus.integrations?.map((i) => i.githubAccountName) ?? []
51
- );
52
- const availableIntegrations = existingIntegrations.filter(
53
- (i) => !alreadyConnectedNames.has(i.githubAccountName)
54
- );
55
-
56
- if (availableIntegrations.length > 0) {
57
- tui.newline();
58
-
59
- const integrationChoices = availableIntegrations.map((i) => ({
60
- name: i.id,
61
- message: `${i.githubAccountName} ${tui.muted(`(from ${i.orgName})`)}`,
62
- }));
63
-
64
- console.log(tui.muted('Press enter with none selected to add a new account'));
35
+ if (!currentStatus.connected || !currentStatus.identity) {
65
36
  tui.newline();
37
+ tui.error(
38
+ `No GitHub identity connected. Run ${tui.bold('agentuity git identity connect')} first.`
39
+ );
40
+ return { added: false };
41
+ }
66
42
 
67
- const selectResponse = await enquirer.prompt<{ integrationIds: string[] }>({
68
- type: 'multiselect',
69
- name: 'integrationIds',
70
- message: 'Select GitHub accounts to add',
71
- choices: integrationChoices,
72
- });
73
-
74
- if (selectResponse.integrationIds.length > 0) {
75
- const selectedIntegrations = availableIntegrations.filter((i) =>
76
- selectResponse.integrationIds.includes(i.id)
77
- );
78
-
79
- const accountNames = selectedIntegrations.map((i) => i.githubAccountName).join(', ');
80
-
81
- const confirmResponse = await enquirer.prompt<{ confirm: boolean }>({
82
- type: 'confirm',
83
- name: 'confirm',
84
- message: `Add ${tui.bold(accountNames)} to ${tui.bold(orgDisplay)}?`,
85
- initial: true,
86
- });
87
-
88
- if (confirmResponse.confirm) {
89
- await tui.spinner({
90
- message: `Adding ${selectedIntegrations.length} GitHub account${selectedIntegrations.length > 1 ? 's' : ''}...`,
91
- clearOnSuccess: true,
92
- callback: async () => {
93
- for (const integration of selectedIntegrations) {
94
- await copyGithubIntegration(apiClient, integration.orgId, orgId);
95
- }
96
- },
97
- });
43
+ const initialCount = currentStatus.installations?.length ?? 0;
98
44
 
99
- tui.newline();
100
- tui.success(
101
- `Added GitHub account${selectedIntegrations.length > 1 ? 's' : ''} to ${tui.bold(orgDisplay)}`
102
- );
103
- return { connected: true };
104
- }
105
- }
106
- }
45
+ tui.newline();
46
+ tui.info(
47
+ `Connected as ${tui.bold(currentStatus.identity.githubUsername)}. Opening GitHub to install the app on a new account...`
48
+ );
49
+ tui.newline();
107
50
 
108
51
  const startResult = await tui.spinner({
109
- message: 'Getting GitHub authorization URL...',
52
+ message: 'Getting GitHub installation URL...',
110
53
  clearOnSuccess: true,
111
- callback: () => startGithubIntegration(apiClient, orgId),
54
+ callback: () => startGithubIntegration(apiClient),
112
55
  });
113
56
 
114
57
  if (!startResult) {
115
- tui.error('Failed to start GitHub authorization');
116
- return { connected: false };
58
+ tui.error('Failed to start GitHub installation flow');
59
+ return { added: false };
117
60
  }
118
61
 
119
62
  const { shortId } = startResult;
@@ -124,19 +67,19 @@ export async function runGitAccountConnect(
124
67
 
125
68
  tui.newline();
126
69
  if (copied) {
127
- console.log('GitHub authorization URL copied to clipboard! Open it in your browser:');
70
+ tui.output('GitHub installation URL copied to clipboard! Open it in your browser:');
128
71
  } else {
129
- console.log('Open this URL in your browser to authorize GitHub access:');
72
+ tui.output('Open this URL in your browser to install the GitHub App:');
130
73
  }
131
74
  tui.newline();
132
- console.log(` ${tui.link(url)}`);
75
+ tui.output(` ${tui.link(url)}`);
133
76
  tui.newline();
134
- console.log(tui.muted('Press Enter to open in your browser, or Ctrl+C to cancel'));
77
+ tui.output(tui.muted('Press Enter to open in your browser, or Ctrl+C to cancel'));
135
78
  tui.newline();
136
79
 
137
80
  const result = await tui.spinner({
138
81
  type: 'countdown',
139
- message: 'Waiting for GitHub authorization',
82
+ message: 'Waiting for GitHub App installation',
140
83
  timeoutMs: 600000,
141
84
  clearOnSuccess: true,
142
85
  onEnterPress: () => {
@@ -152,17 +95,17 @@ export async function runGitAccountConnect(
152
95
  }
153
96
  },
154
97
  callback: async () => {
155
- return await pollForGithubIntegration(apiClient, orgId, initialCount);
98
+ return await pollForGithubIntegration(apiClient, initialCount);
156
99
  },
157
100
  });
158
101
 
159
102
  tui.newline();
160
103
  if (result.connected) {
161
- tui.success(`GitHub account added to ${tui.bold(orgDisplay)}`);
162
- return { connected: true };
104
+ tui.success('GitHub App installed on new account');
105
+ return { added: true };
163
106
  }
164
107
 
165
- return { connected: false };
108
+ return { added: false };
166
109
  } catch (error) {
167
110
  const isCancel =
168
111
  error === '' ||
@@ -171,7 +114,7 @@ export async function runGitAccountConnect(
171
114
  if (isCancel) {
172
115
  tui.newline();
173
116
  tui.info('Cancelled');
174
- return { connected: false, cancelled: true };
117
+ return { added: false, cancelled: true };
175
118
  }
176
119
 
177
120
  logger.trace(error);
@@ -179,18 +122,15 @@ export async function runGitAccountConnect(
179
122
  }
180
123
  }
181
124
 
182
- const AddOptionsSchema = z.object({
183
- org: z.string().optional().describe('Organization ID to add the account to'),
184
- });
125
+ const AddOptionsSchema = z.object({});
185
126
 
186
127
  const AddResponseSchema = z.object({
187
- connected: z.boolean().describe('Whether the account was connected'),
188
- orgId: z.string().optional().describe('Organization ID'),
128
+ added: z.boolean().describe('Whether the installation was added'),
189
129
  });
190
130
 
191
131
  export const addSubcommand = createSubcommand({
192
132
  name: 'add',
193
- description: 'Add a GitHub account to your organization',
133
+ description: 'Install the GitHub App on a new account or organization',
194
134
  tags: ['mutating', 'creates-resource', 'slow', 'api-intensive'],
195
135
  idempotent: false,
196
136
  requires: { auth: true, apiClient: true },
@@ -201,100 +141,21 @@ export const addSubcommand = createSubcommand({
201
141
  examples: [
202
142
  {
203
143
  command: getCommand('git account add'),
204
- description: 'Add a GitHub account to your organization',
205
- },
206
- {
207
- command: getCommand('git account add --org org_abc123'),
208
- description: 'Add to a specific organization',
144
+ description: 'Install the GitHub App on a new account',
209
145
  },
210
146
  ],
211
147
 
212
148
  async handler(ctx) {
213
- const { logger, apiClient, config, opts } = ctx;
149
+ const { logger, apiClient, config } = ctx;
214
150
 
215
151
  try {
216
- const orgs = await tui.spinner({
217
- message: 'Fetching organizations...',
218
- clearOnSuccess: true,
219
- callback: () => listOrganizations(apiClient),
220
- });
221
-
222
- if (orgs.length === 0) {
223
- tui.fatal('No organizations found for your account');
224
- }
225
-
226
- let orgId = opts.org;
227
- let selectedOrg: (typeof orgs)[0] | undefined;
228
-
229
- if (orgId) {
230
- selectedOrg = orgs.find((o) => o.id === orgId);
231
- if (!selectedOrg) {
232
- tui.fatal(`Organization ${orgId} not found`);
233
- }
234
- } else {
235
- const orgStatuses = await tui.spinner({
236
- message: 'Checking GitHub integration status...',
237
- clearOnSuccess: true,
238
- callback: async () => {
239
- const statuses = await Promise.all(
240
- orgs.map(async (org) => {
241
- const status = await getGithubIntegrationStatus(apiClient, org.id);
242
- return {
243
- ...org,
244
- connected: status.connected,
245
- integrations: status.integrations,
246
- };
247
- })
248
- );
249
- return statuses;
250
- },
251
- });
252
-
253
- const sortedOrgs = [...orgStatuses].sort((a, b) => a.name.localeCompare(b.name));
254
-
255
- const firstOrg = orgs[0];
256
- if (orgs.length === 1 && firstOrg) {
257
- orgId = firstOrg.id;
258
- selectedOrg = firstOrg;
259
- } else {
260
- const choices = sortedOrgs.map((org) => {
261
- const count = org.integrations.length;
262
- const suffix =
263
- count > 0
264
- ? tui.muted(` (${count} GitHub account${count > 1 ? 's' : ''})`)
265
- : '';
266
- return {
267
- name: org.name,
268
- message: `${org.name}${suffix}`,
269
- value: org.id,
270
- };
271
- });
272
-
273
- const response = await enquirer.prompt<{ orgName: string }>({
274
- type: 'select',
275
- name: 'orgName',
276
- message: 'Select an organization',
277
- choices,
278
- result(name: string) {
279
- // @ts-expect-error - this.map exists at runtime
280
- return this.map(name)[name];
281
- },
282
- });
283
-
284
- orgId = response.orgName;
285
- selectedOrg = sortedOrgs.find((o) => o.id === orgId);
286
- }
287
- }
288
-
289
- const result = await runGitAccountConnect({
152
+ const result = await runGitAccountAdd({
290
153
  apiClient,
291
- orgId: orgId!,
292
- orgName: selectedOrg?.name,
293
154
  logger,
294
155
  config,
295
156
  });
296
157
 
297
- return { connected: result.connected, orgId };
158
+ return { added: result.added };
298
159
  } catch (error) {
299
160
  const isCancel =
300
161
  error === '' ||
@@ -304,7 +165,7 @@ export const addSubcommand = createSubcommand({
304
165
  if (isCancel) {
305
166
  tui.newline();
306
167
  tui.info('Cancelled');
307
- return { connected: false };
168
+ return { added: false };
308
169
  }
309
170
 
310
171
  logger.trace(error);
@@ -1,12 +1,10 @@
1
1
  import { createCommand } from '../../../types';
2
2
  import { addSubcommand } from './add';
3
- import { removeSubcommand } from './remove';
4
3
  import { listSubcommand } from './list';
4
+ import { removeSubcommand } from './remove';
5
5
 
6
6
  export const accountCommand = createCommand({
7
7
  name: 'account',
8
- description: 'Manage GitHub accounts connected to your organization',
9
- subcommands: [addSubcommand, removeSubcommand, listSubcommand],
8
+ description: 'Manage GitHub App installations',
9
+ subcommands: [addSubcommand, listSubcommand, removeSubcommand],
10
10
  });
11
-
12
- export default accountCommand;
@@ -1,28 +1,23 @@
1
- import { createSubcommand } from '../../../types';
2
- import * as tui from '../../../tui';
1
+ import { z } from 'zod';
3
2
  import { getCommand } from '../../../command-prefix';
4
3
  import { ErrorCode } from '../../../errors';
5
- import { listOrganizations } from '@agentuity/server';
4
+ import * as tui from '../../../tui';
5
+ import { createSubcommand } from '../../../types';
6
6
  import { getGithubIntegrationStatus } from '../api';
7
- import { z } from 'zod';
8
7
 
9
- const ListResponseSchema = z.array(
10
- z.object({
11
- orgId: z.string().describe('Organization ID'),
12
- orgName: z.string().describe('Organization name'),
13
- integrations: z.array(
14
- z.object({
15
- id: z.string().describe('Integration ID'),
16
- githubAccountName: z.string().describe('GitHub account name'),
17
- githubAccountType: z.enum(['user', 'org']).describe('Account type'),
18
- })
19
- ),
20
- })
21
- );
8
+ const ListResponseSchema = z.object({
9
+ installations: z.array(
10
+ z.object({
11
+ installationId: z.string().describe('Installation ID'),
12
+ accountName: z.string().describe('GitHub account name'),
13
+ accountType: z.enum(['User', 'Organization']).describe('Account type'),
14
+ })
15
+ ),
16
+ });
22
17
 
23
18
  export const listSubcommand = createSubcommand({
24
19
  name: 'list',
25
- description: 'List GitHub accounts connected to your organizations',
20
+ description: 'List GitHub App installations',
26
21
  aliases: ['ls'],
27
22
  tags: ['read-only'],
28
23
  idempotent: true,
@@ -33,11 +28,11 @@ export const listSubcommand = createSubcommand({
33
28
  examples: [
34
29
  {
35
30
  command: getCommand('git account list'),
36
- description: 'List all connected GitHub accounts',
31
+ description: 'List GitHub App installations',
37
32
  },
38
33
  {
39
34
  command: getCommand('--json git account list'),
40
- description: 'List accounts in JSON format',
35
+ description: 'List installations in JSON format',
41
36
  },
42
37
  ],
43
38
 
@@ -45,92 +40,66 @@ export const listSubcommand = createSubcommand({
45
40
  const { logger, apiClient, options } = ctx;
46
41
 
47
42
  try {
48
- // Fetch organizations
49
- const orgs = await tui.spinner({
50
- message: 'Fetching organizations...',
43
+ const status = await tui.spinner({
44
+ message: 'Checking GitHub connection...',
51
45
  clearOnSuccess: true,
52
- callback: () => listOrganizations(apiClient),
46
+ callback: () => getGithubIntegrationStatus(apiClient),
53
47
  });
54
48
 
55
- if (orgs.length === 0) {
56
- tui.fatal('No organizations found for your account');
57
- }
58
-
59
- // Check GitHub status for each org
60
- const orgStatuses = await tui.spinner({
61
- message: 'Checking GitHub integration status...',
62
- clearOnSuccess: true,
63
- callback: async () => {
64
- const statuses = await Promise.all(
65
- orgs.map(async (org) => {
66
- const status = await getGithubIntegrationStatus(apiClient, org.id);
67
- return {
68
- ...org,
69
- connected: status.connected,
70
- integrations: status.integrations,
71
- };
72
- })
73
- );
74
- return statuses;
75
- },
76
- });
77
-
78
- // Sort orgs alphabetically
79
- const sortedOrgs = [...orgStatuses].sort((a, b) => a.name.localeCompare(b.name));
80
-
81
- const result = sortedOrgs.map((org) => ({
82
- orgId: org.id,
83
- orgName: org.name,
84
- integrations: org.integrations.map((i) => ({
85
- id: i.id,
86
- githubAccountName: i.githubAccountName,
87
- githubAccountType: i.githubAccountType,
49
+ const result = {
50
+ installations: status.installations.map((i) => ({
51
+ installationId: i.installationId,
52
+ accountName: i.accountName,
53
+ accountType: i.accountType,
88
54
  })),
89
- }));
55
+ };
90
56
 
91
57
  if (options.json) {
92
58
  return result;
93
59
  }
94
60
 
95
61
  tui.newline();
96
- console.log(tui.bold('GitHub Accounts'));
97
- tui.newline();
98
-
99
- let totalCount = 0;
100
62
 
101
- for (const org of sortedOrgs) {
102
- if (org.integrations.length === 0) {
103
- console.log(` ${org.name} ${tui.muted('(no accounts connected)')}`);
104
- } else {
105
- console.log(` ${org.name}`);
106
- for (const integration of org.integrations) {
107
- const typeLabel = integration.githubAccountType === 'org' ? 'org' : 'user';
108
- console.log(
109
- ` ${tui.colorSuccess('✓')} ${integration.githubAccountName} ${tui.muted(`(${typeLabel})`)}`
110
- );
111
- totalCount++;
112
- }
113
- }
63
+ if (!status.connected || !status.identity) {
64
+ tui.output(
65
+ tui.muted(
66
+ `No GitHub identity connected. Run ${tui.bold('agentuity git identity connect')} to connect one.`
67
+ )
68
+ );
69
+ tui.newline();
70
+ return result;
114
71
  }
115
72
 
73
+ tui.output(tui.bold('GitHub App Installations'));
116
74
  tui.newline();
117
- if (totalCount === 0) {
118
- console.log(
75
+
76
+ if (status.installations.length === 0) {
77
+ tui.output(
119
78
  tui.muted(
120
- `No GitHub accounts connected. Run ${tui.bold('agentuity git account add')} to add one.`
79
+ `No installations found. Run ${tui.bold('agentuity git account add')} to install the GitHub App.`
121
80
  )
122
81
  );
123
82
  } else {
124
- console.log(
125
- tui.muted(`${totalCount} GitHub account${totalCount > 1 ? 's' : ''} connected`)
126
- );
83
+ for (const installation of status.installations) {
84
+ const typeLabel = installation.accountType === 'Organization' ? 'org' : 'user';
85
+ tui.output(
86
+ ` ${tui.colorSuccess('✓')} ${installation.accountName} ${tui.muted(`(${typeLabel})`)}`
87
+ );
88
+ }
127
89
  }
128
90
 
91
+ tui.newline();
92
+ tui.output(
93
+ tui.muted(
94
+ `${status.installations.length} installation${status.installations.length !== 1 ? 's' : ''}`
95
+ )
96
+ );
97
+
129
98
  return result;
130
99
  } catch (error) {
131
100
  logger.trace(error);
132
101
  return logger.fatal(
133
- 'Failed to list GitHub accounts: %s',
102
+ 'Failed to list GitHub installations: %s',
134
103
  error,
135
104
  ErrorCode.INTEGRATION_FAILED
136
105
  );