@agentuity/cli 0.0.109 → 0.0.111

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 (253) hide show
  1. package/dist/build-report.d.ts +201 -0
  2. package/dist/build-report.d.ts.map +1 -0
  3. package/dist/build-report.js +335 -0
  4. package/dist/build-report.js.map +1 -0
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +19 -4
  7. package/dist/cli.js.map +1 -1
  8. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  9. package/dist/cmd/build/entry-generator.js +3 -1
  10. package/dist/cmd/build/entry-generator.js.map +1 -1
  11. package/dist/cmd/build/index.d.ts.map +1 -1
  12. package/dist/cmd/build/index.js +44 -1
  13. package/dist/cmd/build/index.js.map +1 -1
  14. package/dist/cmd/build/typecheck.d.ts +7 -1
  15. package/dist/cmd/build/typecheck.d.ts.map +1 -1
  16. package/dist/cmd/build/typecheck.js +11 -1
  17. package/dist/cmd/build/typecheck.js.map +1 -1
  18. package/dist/cmd/build/vite/agent-discovery.d.ts +1 -1
  19. package/dist/cmd/build/vite/agent-discovery.d.ts.map +1 -1
  20. package/dist/cmd/build/vite/agent-discovery.js +3 -3
  21. package/dist/cmd/build/vite/agent-discovery.js.map +1 -1
  22. package/dist/cmd/build/vite/index.d.ts +2 -1
  23. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  24. package/dist/cmd/build/vite/index.js +3 -2
  25. package/dist/cmd/build/vite/index.js.map +1 -1
  26. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  27. package/dist/cmd/build/vite/metadata-generator.js +3 -5
  28. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  29. package/dist/cmd/build/vite/registry-generator.d.ts +1 -1
  30. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  31. package/dist/cmd/build/vite/registry-generator.js +126 -41
  32. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  33. package/dist/cmd/build/vite/route-discovery.d.ts +6 -0
  34. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  35. package/dist/cmd/build/vite/route-discovery.js +19 -0
  36. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  37. package/dist/cmd/build/vite/vite-builder.d.ts +3 -0
  38. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  39. package/dist/cmd/build/vite/vite-builder.js +10 -2
  40. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  41. package/dist/cmd/build/vite-bundler.d.ts +3 -0
  42. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  43. package/dist/cmd/build/vite-bundler.js +14 -5
  44. package/dist/cmd/build/vite-bundler.js.map +1 -1
  45. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  46. package/dist/cmd/cloud/deploy.js +149 -10
  47. package/dist/cmd/cloud/deploy.js.map +1 -1
  48. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  49. package/dist/cmd/cloud/deployment/show.js +0 -1
  50. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  51. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  52. package/dist/cmd/cloud/sandbox/create.js +18 -0
  53. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  54. package/dist/cmd/cloud/sandbox/delete.d.ts.map +1 -1
  55. package/dist/cmd/cloud/sandbox/delete.js +2 -6
  56. package/dist/cmd/cloud/sandbox/delete.js.map +1 -1
  57. package/dist/cmd/cloud/sandbox/download.d.ts +3 -0
  58. package/dist/cmd/cloud/sandbox/download.d.ts.map +1 -0
  59. package/dist/cmd/cloud/sandbox/download.js +89 -0
  60. package/dist/cmd/cloud/sandbox/download.js.map +1 -0
  61. package/dist/cmd/cloud/sandbox/env.d.ts +3 -0
  62. package/dist/cmd/cloud/sandbox/env.d.ts.map +1 -0
  63. package/dist/cmd/cloud/sandbox/env.js +90 -0
  64. package/dist/cmd/cloud/sandbox/env.js.map +1 -0
  65. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  66. package/dist/cmd/cloud/sandbox/get.js +5 -0
  67. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  68. package/dist/cmd/cloud/sandbox/index.d.ts.map +1 -1
  69. package/dist/cmd/cloud/sandbox/index.js +14 -0
  70. package/dist/cmd/cloud/sandbox/index.js.map +1 -1
  71. package/dist/cmd/cloud/sandbox/ls.d.ts +3 -0
  72. package/dist/cmd/cloud/sandbox/ls.d.ts.map +1 -0
  73. package/dist/cmd/cloud/sandbox/ls.js +119 -0
  74. package/dist/cmd/cloud/sandbox/ls.js.map +1 -0
  75. package/dist/cmd/cloud/sandbox/mkdir.d.ts +3 -0
  76. package/dist/cmd/cloud/sandbox/mkdir.d.ts.map +1 -0
  77. package/dist/cmd/cloud/sandbox/mkdir.js +59 -0
  78. package/dist/cmd/cloud/sandbox/mkdir.js.map +1 -0
  79. package/dist/cmd/cloud/sandbox/rm.d.ts +3 -0
  80. package/dist/cmd/cloud/sandbox/rm.d.ts.map +1 -0
  81. package/dist/cmd/cloud/sandbox/rm.js +45 -0
  82. package/dist/cmd/cloud/sandbox/rm.js.map +1 -0
  83. package/dist/cmd/cloud/sandbox/rmdir.d.ts +3 -0
  84. package/dist/cmd/cloud/sandbox/rmdir.d.ts.map +1 -0
  85. package/dist/cmd/cloud/sandbox/rmdir.js +59 -0
  86. package/dist/cmd/cloud/sandbox/rmdir.js.map +1 -0
  87. package/dist/cmd/cloud/sandbox/snapshot/create.d.ts.map +1 -1
  88. package/dist/cmd/cloud/sandbox/snapshot/create.js +0 -2
  89. package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
  90. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  91. package/dist/cmd/cloud/sandbox/snapshot/get.js +0 -2
  92. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  93. package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
  94. package/dist/cmd/cloud/sandbox/snapshot/list.js +0 -3
  95. package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
  96. package/dist/cmd/cloud/sandbox/upload.d.ts +3 -0
  97. package/dist/cmd/cloud/sandbox/upload.d.ts.map +1 -0
  98. package/dist/cmd/cloud/sandbox/upload.js +77 -0
  99. package/dist/cmd/cloud/sandbox/upload.js.map +1 -0
  100. package/dist/cmd/dev/index.d.ts.map +1 -1
  101. package/dist/cmd/dev/index.js +126 -23
  102. package/dist/cmd/dev/index.js.map +1 -1
  103. package/dist/cmd/dev/sync.d.ts.map +1 -1
  104. package/dist/cmd/dev/sync.js +8 -14
  105. package/dist/cmd/dev/sync.js.map +1 -1
  106. package/dist/cmd/git/account/add.d.ts +17 -0
  107. package/dist/cmd/git/account/add.d.ts.map +1 -0
  108. package/dist/cmd/git/account/add.js +244 -0
  109. package/dist/cmd/git/account/add.js.map +1 -0
  110. package/dist/cmd/git/account/index.d.ts +3 -0
  111. package/dist/cmd/git/account/index.d.ts.map +1 -0
  112. package/dist/cmd/git/account/index.js +11 -0
  113. package/dist/cmd/git/account/index.js.map +1 -0
  114. package/dist/cmd/git/account/list.d.ts +2 -0
  115. package/dist/cmd/git/account/list.d.ts.map +1 -0
  116. package/dist/cmd/git/account/list.js +111 -0
  117. package/dist/cmd/git/account/list.js.map +1 -0
  118. package/dist/cmd/git/account/remove.d.ts +2 -0
  119. package/dist/cmd/git/account/remove.d.ts.map +1 -0
  120. package/dist/cmd/git/account/remove.js +171 -0
  121. package/dist/cmd/git/account/remove.js.map +1 -0
  122. package/dist/cmd/git/index.d.ts +3 -0
  123. package/dist/cmd/git/index.d.ts.map +1 -0
  124. package/dist/cmd/git/index.js +19 -0
  125. package/dist/cmd/git/index.js.map +1 -0
  126. package/dist/cmd/git/link.d.ts +32 -0
  127. package/dist/cmd/git/link.d.ts.map +1 -0
  128. package/dist/cmd/git/link.js +357 -0
  129. package/dist/cmd/git/link.js.map +1 -0
  130. package/dist/cmd/git/list.d.ts +2 -0
  131. package/dist/cmd/git/list.d.ts.map +1 -0
  132. package/dist/cmd/git/list.js +137 -0
  133. package/dist/cmd/git/list.js.map +1 -0
  134. package/dist/cmd/git/status.d.ts +2 -0
  135. package/dist/cmd/git/status.d.ts.map +1 -0
  136. package/dist/cmd/git/status.js +119 -0
  137. package/dist/cmd/git/status.js.map +1 -0
  138. package/dist/cmd/git/unlink.d.ts +2 -0
  139. package/dist/cmd/git/unlink.d.ts.map +1 -0
  140. package/dist/cmd/git/unlink.js +98 -0
  141. package/dist/cmd/git/unlink.js.map +1 -0
  142. package/dist/cmd/index.d.ts.map +1 -1
  143. package/dist/cmd/index.js +2 -0
  144. package/dist/cmd/index.js.map +1 -1
  145. package/dist/cmd/integration/api.d.ts +61 -0
  146. package/dist/cmd/integration/api.d.ts.map +1 -0
  147. package/dist/cmd/integration/api.js +176 -0
  148. package/dist/cmd/integration/api.js.map +1 -0
  149. package/dist/cmd/integration/github/connect.d.ts +2 -0
  150. package/dist/cmd/integration/github/connect.d.ts.map +1 -0
  151. package/dist/cmd/integration/github/connect.js +197 -0
  152. package/dist/cmd/integration/github/connect.js.map +1 -0
  153. package/dist/cmd/integration/github/disconnect.d.ts +2 -0
  154. package/dist/cmd/integration/github/disconnect.d.ts.map +1 -0
  155. package/dist/cmd/integration/github/disconnect.js +121 -0
  156. package/dist/cmd/integration/github/disconnect.js.map +1 -0
  157. package/dist/cmd/integration/github/index.d.ts +2 -0
  158. package/dist/cmd/integration/github/index.d.ts.map +1 -0
  159. package/dist/cmd/integration/github/index.js +21 -0
  160. package/dist/cmd/integration/github/index.js.map +1 -0
  161. package/dist/cmd/integration/index.d.ts +2 -0
  162. package/dist/cmd/integration/index.d.ts.map +1 -0
  163. package/dist/cmd/integration/index.js +16 -0
  164. package/dist/cmd/integration/index.js.map +1 -0
  165. package/dist/cmd/project/auth/generate.d.ts +5 -0
  166. package/dist/cmd/project/auth/generate.d.ts.map +1 -0
  167. package/dist/cmd/project/auth/generate.js +102 -0
  168. package/dist/cmd/project/auth/generate.js.map +1 -0
  169. package/dist/cmd/project/auth/index.d.ts +2 -0
  170. package/dist/cmd/project/auth/index.d.ts.map +1 -0
  171. package/dist/cmd/project/auth/index.js +21 -0
  172. package/dist/cmd/project/auth/index.js.map +1 -0
  173. package/dist/cmd/project/auth/init.d.ts +2 -0
  174. package/dist/cmd/project/auth/init.d.ts.map +1 -0
  175. package/dist/cmd/project/auth/init.js +220 -0
  176. package/dist/cmd/project/auth/init.js.map +1 -0
  177. package/dist/cmd/project/auth/shared.d.ts +88 -0
  178. package/dist/cmd/project/auth/shared.d.ts.map +1 -0
  179. package/dist/cmd/project/auth/shared.js +435 -0
  180. package/dist/cmd/project/auth/shared.js.map +1 -0
  181. package/dist/cmd/project/index.d.ts.map +1 -1
  182. package/dist/cmd/project/index.js +9 -1
  183. package/dist/cmd/project/index.js.map +1 -1
  184. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  185. package/dist/cmd/project/template-flow.js +106 -0
  186. package/dist/cmd/project/template-flow.js.map +1 -1
  187. package/dist/config.d.ts +2 -0
  188. package/dist/config.d.ts.map +1 -1
  189. package/dist/config.js +24 -0
  190. package/dist/config.js.map +1 -1
  191. package/dist/errors.d.ts +2 -1
  192. package/dist/errors.d.ts.map +1 -1
  193. package/dist/errors.js +5 -0
  194. package/dist/errors.js.map +1 -1
  195. package/dist/types.d.ts +3 -4
  196. package/dist/types.d.ts.map +1 -1
  197. package/dist/types.js +5 -2
  198. package/dist/types.js.map +1 -1
  199. package/package.json +6 -5
  200. package/src/build-report.ts +457 -0
  201. package/src/cli.ts +20 -4
  202. package/src/cmd/build/entry-generator.ts +3 -1
  203. package/src/cmd/build/index.ts +51 -1
  204. package/src/cmd/build/typecheck.ts +19 -1
  205. package/src/cmd/build/vite/agent-discovery.ts +4 -4
  206. package/src/cmd/build/vite/index.ts +5 -2
  207. package/src/cmd/build/vite/metadata-generator.ts +5 -7
  208. package/src/cmd/build/vite/registry-generator.ts +136 -43
  209. package/src/cmd/build/vite/route-discovery.ts +20 -0
  210. package/src/cmd/build/vite/vite-builder.ts +13 -2
  211. package/src/cmd/build/vite-bundler.ts +17 -4
  212. package/src/cmd/cloud/deploy.ts +183 -12
  213. package/src/cmd/cloud/deployment/show.ts +0 -1
  214. package/src/cmd/cloud/sandbox/create.ts +22 -0
  215. package/src/cmd/cloud/sandbox/delete.ts +2 -6
  216. package/src/cmd/cloud/sandbox/download.ts +96 -0
  217. package/src/cmd/cloud/sandbox/env.ts +104 -0
  218. package/src/cmd/cloud/sandbox/get.ts +5 -0
  219. package/src/cmd/cloud/sandbox/index.ts +14 -0
  220. package/src/cmd/cloud/sandbox/ls.ts +126 -0
  221. package/src/cmd/cloud/sandbox/mkdir.ts +65 -0
  222. package/src/cmd/cloud/sandbox/rm.ts +51 -0
  223. package/src/cmd/cloud/sandbox/rmdir.ts +65 -0
  224. package/src/cmd/cloud/sandbox/snapshot/create.ts +0 -2
  225. package/src/cmd/cloud/sandbox/snapshot/get.ts +0 -2
  226. package/src/cmd/cloud/sandbox/snapshot/list.ts +0 -3
  227. package/src/cmd/cloud/sandbox/upload.ts +83 -0
  228. package/src/cmd/dev/index.ts +147 -33
  229. package/src/cmd/dev/sync.ts +26 -30
  230. package/src/cmd/git/account/add.ts +317 -0
  231. package/src/cmd/git/account/index.ts +12 -0
  232. package/src/cmd/git/account/list.ts +139 -0
  233. package/src/cmd/git/account/remove.ts +212 -0
  234. package/src/cmd/git/index.ts +20 -0
  235. package/src/cmd/git/link.ts +468 -0
  236. package/src/cmd/git/list.ts +161 -0
  237. package/src/cmd/git/status.ts +144 -0
  238. package/src/cmd/git/unlink.ts +117 -0
  239. package/src/cmd/index.ts +2 -0
  240. package/src/cmd/integration/api.ts +379 -0
  241. package/src/cmd/integration/github/connect.ts +242 -0
  242. package/src/cmd/integration/github/disconnect.ts +149 -0
  243. package/src/cmd/integration/github/index.ts +21 -0
  244. package/src/cmd/integration/index.ts +16 -0
  245. package/src/cmd/project/auth/generate.ts +116 -0
  246. package/src/cmd/project/auth/index.ts +21 -0
  247. package/src/cmd/project/auth/init.ts +263 -0
  248. package/src/cmd/project/auth/shared.ts +534 -0
  249. package/src/cmd/project/index.ts +9 -1
  250. package/src/cmd/project/template-flow.ts +125 -0
  251. package/src/config.ts +34 -0
  252. package/src/errors.ts +7 -0
  253. package/src/types.ts +5 -2
@@ -0,0 +1,317 @@
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
+ import type { Logger } from '@agentuity/core';
11
+ import { z } from 'zod';
12
+ import {
13
+ startGithubIntegration,
14
+ pollForGithubIntegration,
15
+ getGithubIntegrationStatus,
16
+ getExistingGithubIntegrations,
17
+ copyGithubIntegration,
18
+ } from '../../integration/api';
19
+
20
+ export interface RunGitAccountConnectOptions {
21
+ apiClient: APIClient;
22
+ orgId: string;
23
+ orgName?: string;
24
+ logger: Logger;
25
+ config?: Config | null;
26
+ }
27
+
28
+ export interface RunGitAccountConnectResult {
29
+ connected: boolean;
30
+ cancelled?: boolean;
31
+ }
32
+
33
+ export async function runGitAccountConnect(
34
+ options: RunGitAccountConnectOptions
35
+ ): Promise<RunGitAccountConnectResult> {
36
+ const { apiClient, orgId, orgName, logger, config } = options;
37
+ const orgDisplay = orgName ?? orgId;
38
+
39
+ 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
+ });
48
+
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'));
65
+ tui.newline();
66
+
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
+ });
98
+
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
+ }
107
+
108
+ const startResult = await tui.spinner({
109
+ message: 'Getting GitHub authorization URL...',
110
+ clearOnSuccess: true,
111
+ callback: () => startGithubIntegration(apiClient, orgId),
112
+ });
113
+
114
+ if (!startResult) {
115
+ tui.error('Failed to start GitHub authorization');
116
+ return { connected: false };
117
+ }
118
+
119
+ const { shortId } = startResult;
120
+ const apiBaseUrl = getAPIBaseURL(config);
121
+ const url = `${apiBaseUrl}/github/connect/${shortId}`;
122
+
123
+ const copied = await tui.copyToClipboard(url);
124
+
125
+ tui.newline();
126
+ if (copied) {
127
+ console.log('GitHub authorization URL copied to clipboard! Open it in your browser:');
128
+ } else {
129
+ console.log('Open this URL in your browser to authorize GitHub access:');
130
+ }
131
+ tui.newline();
132
+ console.log(` ${tui.link(url)}`);
133
+ tui.newline();
134
+ console.log(tui.muted('Press Enter to open in your browser, or Ctrl+C to cancel'));
135
+ tui.newline();
136
+
137
+ const result = await tui.spinner({
138
+ type: 'countdown',
139
+ message: 'Waiting for GitHub authorization',
140
+ timeoutMs: 600000,
141
+ clearOnSuccess: true,
142
+ onEnterPress: () => {
143
+ const platform = process.platform;
144
+ if (platform === 'win32') {
145
+ Bun.spawn(['cmd', '/c', 'start', '', url], {
146
+ stdout: 'ignore',
147
+ stderr: 'ignore',
148
+ });
149
+ } else {
150
+ const command = platform === 'darwin' ? 'open' : 'xdg-open';
151
+ Bun.spawn([command, url], { stdout: 'ignore', stderr: 'ignore' });
152
+ }
153
+ },
154
+ callback: async () => {
155
+ return await pollForGithubIntegration(apiClient, orgId, initialCount);
156
+ },
157
+ });
158
+
159
+ tui.newline();
160
+ if (result.connected) {
161
+ tui.success(`GitHub account added to ${tui.bold(orgDisplay)}`);
162
+ return { connected: true };
163
+ }
164
+
165
+ return { connected: false };
166
+ } catch (error) {
167
+ const isCancel =
168
+ error === '' ||
169
+ (error instanceof Error && (error.message === '' || error.message === 'User cancelled'));
170
+
171
+ if (isCancel) {
172
+ tui.newline();
173
+ tui.info('Cancelled');
174
+ return { connected: false, cancelled: true };
175
+ }
176
+
177
+ logger.trace(error);
178
+ throw error;
179
+ }
180
+ }
181
+
182
+ const AddOptionsSchema = z.object({
183
+ org: z.string().optional().describe('Organization ID to add the account to'),
184
+ });
185
+
186
+ const AddResponseSchema = z.object({
187
+ connected: z.boolean().describe('Whether the account was connected'),
188
+ orgId: z.string().optional().describe('Organization ID'),
189
+ });
190
+
191
+ export const addSubcommand = createSubcommand({
192
+ name: 'add',
193
+ description: 'Add a GitHub account to your organization',
194
+ tags: ['mutating', 'creates-resource', 'slow', 'api-intensive'],
195
+ idempotent: false,
196
+ requires: { auth: true, apiClient: true },
197
+ schema: {
198
+ options: AddOptionsSchema,
199
+ response: AddResponseSchema,
200
+ },
201
+ examples: [
202
+ {
203
+ 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',
209
+ },
210
+ ],
211
+
212
+ async handler(ctx) {
213
+ const { logger, apiClient, config, opts } = ctx;
214
+
215
+ 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
+ if (orgs.length === 1) {
256
+ orgId = orgs[0].id;
257
+ selectedOrg = orgs[0];
258
+ } else {
259
+ const choices = sortedOrgs.map((org) => {
260
+ const count = org.integrations.length;
261
+ const suffix =
262
+ count > 0
263
+ ? tui.muted(` (${count} GitHub account${count > 1 ? 's' : ''})`)
264
+ : '';
265
+ return {
266
+ name: org.name,
267
+ message: `${org.name}${suffix}`,
268
+ value: org.id,
269
+ };
270
+ });
271
+
272
+ const response = await enquirer.prompt<{ orgName: string }>({
273
+ type: 'select',
274
+ name: 'orgName',
275
+ message: 'Select an organization',
276
+ choices,
277
+ result(name: string) {
278
+ // @ts-expect-error - this.map exists at runtime
279
+ return this.map(name)[name];
280
+ },
281
+ });
282
+
283
+ orgId = response.orgName;
284
+ selectedOrg = sortedOrgs.find((o) => o.id === orgId);
285
+ }
286
+ }
287
+
288
+ const result = await runGitAccountConnect({
289
+ apiClient,
290
+ orgId: orgId!,
291
+ orgName: selectedOrg?.name,
292
+ logger,
293
+ config,
294
+ });
295
+
296
+ return { connected: result.connected, orgId };
297
+ } catch (error) {
298
+ const isCancel =
299
+ error === '' ||
300
+ (error instanceof Error &&
301
+ (error.message === '' || error.message === 'User cancelled'));
302
+
303
+ if (isCancel) {
304
+ tui.newline();
305
+ tui.info('Cancelled');
306
+ return { connected: false };
307
+ }
308
+
309
+ logger.trace(error);
310
+ return logger.fatal(
311
+ 'Failed to add GitHub account: %s',
312
+ error,
313
+ ErrorCode.INTEGRATION_FAILED
314
+ );
315
+ }
316
+ },
317
+ });
@@ -0,0 +1,12 @@
1
+ import { createCommand } from '../../../types';
2
+ import { addSubcommand } from './add';
3
+ import { removeSubcommand } from './remove';
4
+ import { listSubcommand } from './list';
5
+
6
+ export const accountCommand = createCommand({
7
+ name: 'account',
8
+ description: 'Manage GitHub accounts connected to your organization',
9
+ subcommands: [addSubcommand, removeSubcommand, listSubcommand],
10
+ });
11
+
12
+ export default accountCommand;
@@ -0,0 +1,139 @@
1
+ import { createSubcommand } from '../../../types';
2
+ import * as tui from '../../../tui';
3
+ import { getCommand } from '../../../command-prefix';
4
+ import { ErrorCode } from '../../../errors';
5
+ import { listOrganizations } from '@agentuity/server';
6
+ import { getGithubIntegrationStatus } from '../../integration/api';
7
+ import { z } from 'zod';
8
+
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
+ );
22
+
23
+ export const listSubcommand = createSubcommand({
24
+ name: 'list',
25
+ description: 'List GitHub accounts connected to your organizations',
26
+ aliases: ['ls'],
27
+ tags: ['read-only'],
28
+ idempotent: true,
29
+ requires: { auth: true, apiClient: true },
30
+ schema: {
31
+ response: ListResponseSchema,
32
+ },
33
+ examples: [
34
+ {
35
+ command: getCommand('git account list'),
36
+ description: 'List all connected GitHub accounts',
37
+ },
38
+ {
39
+ command: getCommand('--json git account list'),
40
+ description: 'List accounts in JSON format',
41
+ },
42
+ ],
43
+
44
+ async handler(ctx) {
45
+ const { logger, apiClient, options } = ctx;
46
+
47
+ try {
48
+ // Fetch organizations
49
+ const orgs = await tui.spinner({
50
+ message: 'Fetching organizations...',
51
+ clearOnSuccess: true,
52
+ callback: () => listOrganizations(apiClient),
53
+ });
54
+
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,
88
+ })),
89
+ }));
90
+
91
+ if (options.json) {
92
+ return result;
93
+ }
94
+
95
+ tui.newline();
96
+ console.log(tui.bold('GitHub Accounts'));
97
+ tui.newline();
98
+
99
+ let totalCount = 0;
100
+
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
+ }
114
+ }
115
+
116
+ tui.newline();
117
+ if (totalCount === 0) {
118
+ console.log(
119
+ tui.muted(
120
+ `No GitHub accounts connected. Run ${tui.bold('agentuity git account add')} to add one.`
121
+ )
122
+ );
123
+ } else {
124
+ console.log(
125
+ tui.muted(`${totalCount} GitHub account${totalCount > 1 ? 's' : ''} connected`)
126
+ );
127
+ }
128
+
129
+ return result;
130
+ } catch (error) {
131
+ logger.trace(error);
132
+ return logger.fatal(
133
+ 'Failed to list GitHub accounts: %s',
134
+ error,
135
+ ErrorCode.INTEGRATION_FAILED
136
+ );
137
+ }
138
+ },
139
+ });
@@ -0,0 +1,212 @@
1
+ import { createSubcommand } from '../../../types';
2
+ import * as tui from '../../../tui';
3
+ import { getCommand } from '../../../command-prefix';
4
+ import { ErrorCode } from '../../../errors';
5
+ import { listOrganizations } from '@agentuity/server';
6
+ import enquirer from 'enquirer';
7
+ import { z } from 'zod';
8
+ import {
9
+ getGithubIntegrationStatus,
10
+ disconnectGithubIntegration,
11
+ type GithubIntegration,
12
+ } from '../../integration/api';
13
+
14
+ const RemoveOptionsSchema = z.object({
15
+ org: z.string().optional().describe('Organization ID'),
16
+ account: z.string().optional().describe('GitHub integration ID to remove'),
17
+ confirm: z.boolean().optional().describe('Skip confirmation prompt'),
18
+ });
19
+
20
+ const RemoveResponseSchema = z.object({
21
+ removed: z.boolean().describe('Whether the account was removed'),
22
+ orgId: z.string().optional().describe('Organization ID'),
23
+ integrationId: z.string().optional().describe('Integration ID that was removed'),
24
+ });
25
+
26
+ export const removeSubcommand = createSubcommand({
27
+ name: 'remove',
28
+ description: 'Remove a GitHub account from your organization',
29
+ tags: ['mutating', 'destructive', 'slow'],
30
+ idempotent: false,
31
+ requires: { auth: true, apiClient: true },
32
+ schema: {
33
+ options: RemoveOptionsSchema,
34
+ response: RemoveResponseSchema,
35
+ },
36
+ examples: [
37
+ {
38
+ command: getCommand('git account remove'),
39
+ description: 'Remove a GitHub account from your organization',
40
+ },
41
+ {
42
+ command: getCommand('git account remove --org org_abc --account int_xyz --confirm'),
43
+ description: 'Remove a specific account without prompts',
44
+ },
45
+ {
46
+ command: getCommand('--json git account remove --org org_abc --account int_xyz --confirm'),
47
+ description: 'Remove and return JSON result',
48
+ },
49
+ ],
50
+
51
+ async handler(ctx) {
52
+ const { logger, apiClient, opts, options } = ctx;
53
+
54
+ try {
55
+ // If both org and account provided, skip interactive flow
56
+ if (opts.org && opts.account) {
57
+ if (!opts.confirm) {
58
+ const confirmed = await tui.confirm(
59
+ `Are you sure you want to remove this GitHub account?`
60
+ );
61
+ if (!confirmed) {
62
+ tui.info('Cancelled');
63
+ return { removed: false };
64
+ }
65
+ }
66
+
67
+ await tui.spinner({
68
+ message: 'Removing GitHub account...',
69
+ clearOnSuccess: true,
70
+ callback: () => disconnectGithubIntegration(apiClient, opts.org!, opts.account!),
71
+ });
72
+
73
+ if (!options.json) {
74
+ tui.newline();
75
+ tui.success('Removed GitHub account');
76
+ }
77
+
78
+ return { removed: true, orgId: opts.org, integrationId: opts.account };
79
+ }
80
+
81
+ // Fetch organizations
82
+ const orgs = await tui.spinner({
83
+ message: 'Fetching organizations...',
84
+ clearOnSuccess: true,
85
+ callback: () => listOrganizations(apiClient),
86
+ });
87
+
88
+ if (orgs.length === 0) {
89
+ tui.fatal('No organizations found for your account');
90
+ }
91
+
92
+ // Check GitHub status for each org
93
+ const orgStatuses = await tui.spinner({
94
+ message: 'Checking GitHub integration status...',
95
+ clearOnSuccess: true,
96
+ callback: async () => {
97
+ const statuses = await Promise.all(
98
+ orgs.map(async (org) => {
99
+ const status = await getGithubIntegrationStatus(apiClient, org.id);
100
+ return {
101
+ ...org,
102
+ connected: status.connected,
103
+ integrations: status.integrations,
104
+ };
105
+ })
106
+ );
107
+ return statuses;
108
+ },
109
+ });
110
+
111
+ // Flatten all integrations across orgs
112
+ const allIntegrations: Array<{
113
+ orgId: string;
114
+ orgName: string;
115
+ integration: GithubIntegration;
116
+ }> = [];
117
+
118
+ for (const org of orgStatuses) {
119
+ for (const integration of org.integrations) {
120
+ allIntegrations.push({
121
+ orgId: org.id,
122
+ orgName: org.name,
123
+ integration,
124
+ });
125
+ }
126
+ }
127
+
128
+ if (allIntegrations.length === 0) {
129
+ if (!options.json) {
130
+ tui.newline();
131
+ tui.info('No GitHub accounts are connected.');
132
+ }
133
+ return { removed: false };
134
+ }
135
+
136
+ // Build choices showing GitHub account and org
137
+ const choices = allIntegrations.map((item) => ({
138
+ name: `${tui.bold(item.integration.githubAccountName)} ${tui.muted(`(${item.integration.githubAccountType})`)} → ${tui.bold(item.orgName)}`,
139
+ value: `${item.orgId}:${item.integration.id}`,
140
+ }));
141
+
142
+ // Show picker
143
+ const response = await enquirer.prompt<{ selection: string }>({
144
+ type: 'select',
145
+ name: 'selection',
146
+ message: 'Select a GitHub account to remove',
147
+ choices,
148
+ result(name: string) {
149
+ // Return the value (IDs) instead of the display name
150
+ const choice = choices.find((c) => c.name === name);
151
+ return choice?.value ?? name;
152
+ },
153
+ });
154
+
155
+ const colonIdx = response.selection.indexOf(':');
156
+ if (colonIdx === -1) {
157
+ logger.fatal('Invalid selection format');
158
+ }
159
+ const orgId = response.selection.slice(0, colonIdx);
160
+ const integrationId = response.selection.slice(colonIdx + 1);
161
+ const selected = allIntegrations.find(
162
+ (i) => i.orgId === orgId && i.integration.id === integrationId
163
+ );
164
+ const displayName = selected
165
+ ? `${tui.bold(selected.integration.githubAccountName)} from ${tui.bold(selected.orgName)}`
166
+ : response.selection;
167
+
168
+ // Confirm
169
+ if (!opts.confirm) {
170
+ const confirmed = await tui.confirm(`Are you sure you want to remove ${displayName}?`);
171
+
172
+ if (!confirmed) {
173
+ tui.newline();
174
+ tui.info('Cancelled');
175
+ return { removed: false };
176
+ }
177
+ }
178
+
179
+ await tui.spinner({
180
+ message: 'Removing GitHub account...',
181
+ clearOnSuccess: true,
182
+ callback: () => disconnectGithubIntegration(apiClient, orgId, integrationId),
183
+ });
184
+
185
+ if (!options.json) {
186
+ tui.newline();
187
+ tui.success(`Removed ${displayName}`);
188
+ }
189
+
190
+ return { removed: true, orgId, integrationId };
191
+ } catch (error) {
192
+ // Handle user cancellation (Ctrl+C)
193
+ const isCancel =
194
+ error === '' ||
195
+ (error instanceof Error &&
196
+ (error.message === '' || error.message === 'User cancelled'));
197
+
198
+ if (isCancel) {
199
+ tui.newline();
200
+ tui.info('Cancelled');
201
+ return { removed: false };
202
+ }
203
+
204
+ logger.trace(error);
205
+ return logger.fatal(
206
+ 'Failed to remove GitHub account: %s',
207
+ error,
208
+ ErrorCode.INTEGRATION_FAILED
209
+ );
210
+ }
211
+ },
212
+ });
@@ -0,0 +1,20 @@
1
+ import { createCommand } from '../../types';
2
+ import { accountCommand } from './account';
3
+ import { linkSubcommand } from './link';
4
+ import { listSubcommand } from './list';
5
+ import { unlinkSubcommand } from './unlink';
6
+ import { statusSubcommand } from './status';
7
+
8
+ export const gitCommand = createCommand({
9
+ name: 'git',
10
+ description: 'Manage GitHub integration and repository connections',
11
+ subcommands: [
12
+ accountCommand,
13
+ linkSubcommand,
14
+ listSubcommand,
15
+ unlinkSubcommand,
16
+ statusSubcommand,
17
+ ],
18
+ });
19
+
20
+ export default gitCommand;