@agentuity/cli 0.1.34 → 0.1.35

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 (114) hide show
  1. package/bin/cli.ts +107 -6
  2. package/dist/cmd/ai/index.d.ts.map +1 -1
  3. package/dist/cmd/ai/index.js +0 -6
  4. package/dist/cmd/ai/index.js.map +1 -1
  5. package/dist/cmd/ai/opencode/uninstall.js +1 -1
  6. package/dist/cmd/ai/opencode/uninstall.js.map +1 -1
  7. package/dist/cmd/build/vite/bun-dev-server.d.ts +6 -0
  8. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  9. package/dist/cmd/build/vite/bun-dev-server.js +100 -33
  10. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  11. package/dist/cmd/cloud/apikey/create.d.ts.map +1 -1
  12. package/dist/cmd/cloud/apikey/create.js +1 -1
  13. package/dist/cmd/cloud/apikey/create.js.map +1 -1
  14. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  15. package/dist/cmd/cloud/deploy.js +4 -1
  16. package/dist/cmd/cloud/deploy.js.map +1 -1
  17. package/dist/cmd/cloud/env/delete.d.ts.map +1 -1
  18. package/dist/cmd/cloud/env/delete.js +100 -45
  19. package/dist/cmd/cloud/env/delete.js.map +1 -1
  20. package/dist/cmd/cloud/env/pull.d.ts.map +1 -1
  21. package/dist/cmd/cloud/env/pull.js +1 -1
  22. package/dist/cmd/cloud/env/pull.js.map +1 -1
  23. package/dist/cmd/cloud/machine/delete.js +1 -1
  24. package/dist/cmd/cloud/machine/delete.js.map +1 -1
  25. package/dist/cmd/dev/index.d.ts.map +1 -1
  26. package/dist/cmd/dev/index.js +58 -10
  27. package/dist/cmd/dev/index.js.map +1 -1
  28. package/dist/cmd/index.d.ts.map +1 -1
  29. package/dist/cmd/index.js +1 -0
  30. package/dist/cmd/index.js.map +1 -1
  31. package/dist/cmd/support/index.d.ts +2 -0
  32. package/dist/cmd/support/index.d.ts.map +1 -0
  33. package/dist/cmd/support/index.js +11 -0
  34. package/dist/cmd/support/index.js.map +1 -0
  35. package/dist/cmd/support/logs/index.d.ts +3 -0
  36. package/dist/cmd/support/logs/index.d.ts.map +1 -0
  37. package/dist/cmd/support/logs/index.js +9 -0
  38. package/dist/cmd/support/logs/index.js.map +1 -0
  39. package/dist/cmd/support/logs/path.d.ts +3 -0
  40. package/dist/cmd/support/logs/path.d.ts.map +1 -0
  41. package/dist/cmd/support/logs/path.js +52 -0
  42. package/dist/cmd/support/logs/path.js.map +1 -0
  43. package/dist/cmd/support/logs/show.d.ts +3 -0
  44. package/dist/cmd/support/logs/show.d.ts.map +1 -0
  45. package/dist/cmd/support/logs/show.js +121 -0
  46. package/dist/cmd/support/logs/show.js.map +1 -0
  47. package/dist/cmd/support/report.d.ts +3 -0
  48. package/dist/cmd/support/report.d.ts.map +1 -0
  49. package/dist/cmd/support/report.js +299 -0
  50. package/dist/cmd/support/report.js.map +1 -0
  51. package/dist/cmd/support/system.d.ts +3 -0
  52. package/dist/cmd/support/system.d.ts.map +1 -0
  53. package/dist/cmd/support/system.js +120 -0
  54. package/dist/cmd/support/system.js.map +1 -0
  55. package/dist/cmd/version/index.d.ts.map +1 -1
  56. package/dist/cmd/version/index.js +1 -0
  57. package/dist/cmd/version/index.js.map +1 -1
  58. package/dist/composite-logger.d.ts +35 -0
  59. package/dist/composite-logger.d.ts.map +1 -0
  60. package/dist/composite-logger.js +78 -0
  61. package/dist/composite-logger.js.map +1 -0
  62. package/dist/index.d.ts +2 -0
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +2 -0
  65. package/dist/index.js.map +1 -1
  66. package/dist/internal-logger.d.ts +77 -0
  67. package/dist/internal-logger.d.ts.map +1 -0
  68. package/dist/internal-logger.js +340 -0
  69. package/dist/internal-logger.js.map +1 -0
  70. package/dist/types.d.ts +6 -0
  71. package/dist/types.d.ts.map +1 -1
  72. package/dist/types.js.map +1 -1
  73. package/package.json +6 -6
  74. package/src/cmd/ai/index.ts +0 -6
  75. package/src/cmd/ai/opencode/uninstall.ts +1 -1
  76. package/src/cmd/build/vite/bun-dev-server.ts +113 -36
  77. package/src/cmd/cloud/apikey/create.ts +3 -1
  78. package/src/cmd/cloud/deploy.ts +4 -1
  79. package/src/cmd/cloud/env/delete.ts +100 -45
  80. package/src/cmd/cloud/env/pull.ts +1 -6
  81. package/src/cmd/cloud/machine/delete.ts +1 -1
  82. package/src/cmd/dev/index.ts +59 -11
  83. package/src/cmd/index.ts +1 -0
  84. package/src/cmd/support/index.ts +11 -0
  85. package/src/cmd/support/logs/index.ts +9 -0
  86. package/src/cmd/support/logs/path.ts +56 -0
  87. package/src/cmd/support/logs/show.ts +144 -0
  88. package/src/cmd/support/report.ts +364 -0
  89. package/src/cmd/support/system.ts +130 -0
  90. package/src/cmd/version/index.ts +1 -0
  91. package/src/composite-logger.ts +86 -0
  92. package/src/index.ts +7 -0
  93. package/src/internal-logger.ts +411 -0
  94. package/src/types.ts +6 -0
  95. package/dist/cmd/ai/skills/generate.d.ts +0 -3
  96. package/dist/cmd/ai/skills/generate.d.ts.map +0 -1
  97. package/dist/cmd/ai/skills/generate.js +0 -65
  98. package/dist/cmd/ai/skills/generate.js.map +0 -1
  99. package/dist/cmd/ai/skills/generator.d.ts +0 -4
  100. package/dist/cmd/ai/skills/generator.d.ts.map +0 -1
  101. package/dist/cmd/ai/skills/generator.js +0 -410
  102. package/dist/cmd/ai/skills/generator.js.map +0 -1
  103. package/dist/cmd/ai/skills/index.d.ts +0 -4
  104. package/dist/cmd/ai/skills/index.d.ts.map +0 -1
  105. package/dist/cmd/ai/skills/index.js +0 -21
  106. package/dist/cmd/ai/skills/index.js.map +0 -1
  107. package/dist/cmd/dev/skills.d.ts +0 -10
  108. package/dist/cmd/dev/skills.d.ts.map +0 -1
  109. package/dist/cmd/dev/skills.js +0 -57
  110. package/dist/cmd/dev/skills.js.map +0 -1
  111. package/src/cmd/ai/skills/generate.ts +0 -75
  112. package/src/cmd/ai/skills/generator.ts +0 -527
  113. package/src/cmd/ai/skills/index.ts +0 -23
  114. package/src/cmd/dev/skills.ts +0 -82
@@ -0,0 +1,56 @@
1
+ import { createSubcommand } from '../../../types';
2
+ import { z } from 'zod';
3
+ import { getLatestLogSession, getLogsDirPath } from '../../../internal-logger';
4
+ import * as tui from '../../../tui';
5
+
6
+ const argsSchema = z.object({});
7
+
8
+ const optionsSchema = z.object({
9
+ logs: z.boolean().optional().default(false).describe('Show path to logs directory instead of session'),
10
+ });
11
+
12
+ export default createSubcommand({
13
+ name: 'path',
14
+ description: 'Show the path to the most recent log session',
15
+ schema: {
16
+ args: argsSchema,
17
+ options: optionsSchema,
18
+ },
19
+ handler: async (ctx) => {
20
+ const { opts } = ctx;
21
+ const isJsonMode = ctx.options.json;
22
+
23
+ if (opts.logs) {
24
+ // Show the logs directory path
25
+ const logsDir = getLogsDirPath();
26
+ if (isJsonMode) {
27
+ console.log(JSON.stringify({ logsDir }));
28
+ } else {
29
+ console.log(logsDir);
30
+ }
31
+ return;
32
+ }
33
+
34
+ const sessionDir = getLatestLogSession();
35
+ if (!sessionDir) {
36
+ if (isJsonMode) {
37
+ console.log(
38
+ JSON.stringify({
39
+ error: 'No logs found',
40
+ logsDir: getLogsDirPath(),
41
+ })
42
+ );
43
+ } else {
44
+ tui.warning('No CLI logs found');
45
+ tui.info(`Logs directory: ${tui.muted(getLogsDirPath())}`);
46
+ }
47
+ return;
48
+ }
49
+
50
+ if (isJsonMode) {
51
+ console.log(JSON.stringify({ sessionDir }));
52
+ } else {
53
+ console.log(sessionDir);
54
+ }
55
+ },
56
+ });
@@ -0,0 +1,144 @@
1
+ import { createSubcommand } from '../../../types';
2
+ import { z } from 'zod';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { getLatestLogSession, getLogsDirPath } from '../../../internal-logger';
5
+ import * as tui from '../../../tui';
6
+ import { join } from 'node:path';
7
+
8
+ const argsSchema = z.object({});
9
+
10
+ const optionsSchema = z.object({
11
+ session: z.boolean().optional().default(false).describe('Show session metadata only'),
12
+ tail: z.number().optional().describe('Show last N log entries'),
13
+ });
14
+
15
+ export default createSubcommand({
16
+ name: 'show',
17
+ description: 'Show the most recent CLI command logs',
18
+ schema: {
19
+ args: argsSchema,
20
+ options: optionsSchema,
21
+ },
22
+ handler: async (ctx) => {
23
+ const { opts } = ctx;
24
+ const isJsonMode = ctx.options.json;
25
+
26
+ const sessionDir = getLatestLogSession();
27
+ if (!sessionDir) {
28
+ if (isJsonMode) {
29
+ console.log(
30
+ JSON.stringify({
31
+ error: 'No logs found',
32
+ logsDir: getLogsDirPath(),
33
+ })
34
+ );
35
+ return;
36
+ }
37
+ tui.warning('No CLI logs found');
38
+ tui.info(`Logs are stored in: ${tui.muted(getLogsDirPath())}`);
39
+ return;
40
+ }
41
+
42
+ const sessionFile = join(sessionDir, 'session.json');
43
+ const logsFile = join(sessionDir, 'logs.jsonl');
44
+
45
+ if (!existsSync(sessionFile)) {
46
+ if (isJsonMode) {
47
+ console.log(JSON.stringify({ error: 'Session metadata not found' }));
48
+ return;
49
+ }
50
+ tui.error('Session metadata not found');
51
+ return;
52
+ }
53
+
54
+ // Read session metadata
55
+ const sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8'));
56
+
57
+ if (opts.session) {
58
+ // Show session metadata only
59
+ if (isJsonMode) {
60
+ console.log(JSON.stringify(sessionData, null, 2));
61
+ } else {
62
+ tui.info(tui.bold('CLI Command Session'));
63
+ tui.newline();
64
+ tui.output(`Session ID: ${tui.colorPrimary(sessionData.sessionId)}`);
65
+ tui.output(`Command: ${tui.colorInfo(sessionData.command)}`);
66
+ tui.output(`Args: ${sessionData.args.join(' ')}`);
67
+ tui.output(`Timestamp: ${sessionData.timestamp}`);
68
+ tui.output(`CLI Version: ${sessionData.cli.version}`);
69
+ tui.output(`Platform: ${sessionData.system.platform}/${sessionData.system.arch}`);
70
+ tui.output(`CWD: ${sessionData.cwd}`);
71
+ tui.newline();
72
+ tui.output(tui.muted(`Session directory: ${sessionDir}`));
73
+ }
74
+ return;
75
+ }
76
+
77
+ // Read log entries
78
+ if (!existsSync(logsFile)) {
79
+ if (isJsonMode) {
80
+ console.log(JSON.stringify({ session: sessionData, logs: [] }));
81
+ return;
82
+ }
83
+ tui.warning('No log entries found for this session');
84
+ return;
85
+ }
86
+
87
+ const logLines = readFileSync(logsFile, 'utf-8')
88
+ .split('\n')
89
+ .filter((line) => line.trim());
90
+ const logEntries = logLines.map((line) => JSON.parse(line));
91
+
92
+ // Apply tail filter if specified
93
+ const displayEntries = opts.tail ? logEntries.slice(-opts.tail) : logEntries;
94
+
95
+ if (isJsonMode) {
96
+ console.log(
97
+ JSON.stringify(
98
+ {
99
+ session: sessionData,
100
+ logs: displayEntries,
101
+ },
102
+ null,
103
+ 2
104
+ )
105
+ );
106
+ } else {
107
+ // Human-readable output
108
+ tui.info(tui.bold('CLI Command Session'));
109
+ tui.newline();
110
+ tui.output(`Session ID: ${tui.colorPrimary(sessionData.sessionId)}`);
111
+ tui.output(`Command: ${tui.colorInfo(sessionData.command)}`);
112
+ tui.output(`Timestamp: ${sessionData.timestamp}`);
113
+ tui.output(`Total Logs: ${logEntries.length}`);
114
+ if (opts.tail) {
115
+ tui.output(`Showing: Last ${opts.tail} entries`);
116
+ }
117
+ tui.newline();
118
+
119
+ // Display log entries
120
+ for (const entry of displayEntries) {
121
+ const levelColor =
122
+ entry.level === 'error'
123
+ ? tui.colorError
124
+ : entry.level === 'warn'
125
+ ? tui.warn
126
+ : entry.level === 'info'
127
+ ? tui.colorInfo
128
+ : tui.muted;
129
+
130
+ const timestamp = new Date(entry.timestamp).toLocaleTimeString();
131
+ const level = entry.level.toUpperCase().padEnd(5);
132
+
133
+ tui.output(`${tui.muted(timestamp)} ${levelColor(level)} ${entry.message}`);
134
+
135
+ if (entry.context && Object.keys(entry.context).length > 0) {
136
+ tui.output(tui.muted(` Context: ${JSON.stringify(entry.context)}`));
137
+ }
138
+ }
139
+
140
+ tui.newline();
141
+ tui.output(tui.muted(`Session directory: ${sessionDir}`));
142
+ }
143
+ },
144
+ });
@@ -0,0 +1,364 @@
1
+ import { createSubcommand } from '../../types';
2
+ import { z } from 'zod';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { tmpdir } from 'node:os';
6
+ import { getLatestLogSession } from '../../internal-logger';
7
+ import * as tui from '../../tui';
8
+ import { randomBytes } from 'node:crypto';
9
+ import AdmZip from 'adm-zip';
10
+ import { APIResponseSchema } from '@agentuity/server';
11
+
12
+ const argsSchema = z.object({});
13
+
14
+ const optionsSchema = z.object({
15
+ description: z
16
+ .string()
17
+ .optional()
18
+ .describe('Description of the issue (skips prompt if provided)'),
19
+ noOpen: z
20
+ .boolean()
21
+ .optional()
22
+ .default(false)
23
+ .describe('Do not automatically open GitHub issue in browser'),
24
+ });
25
+
26
+ const ReportUploadDataSchema = z.object({
27
+ presigned_url: z.string(),
28
+ url: z.string(),
29
+ report_id: z.string(),
30
+ expires_in: z.number(),
31
+ });
32
+
33
+ const ReportUploadResponseSchema = APIResponseSchema(ReportUploadDataSchema);
34
+
35
+ /**
36
+ * Create a zip file containing session and logs
37
+ */
38
+ async function createReportZip(sessionDir: string): Promise<string> {
39
+ const sessionFile = join(sessionDir, 'session.json');
40
+ const logsFile = join(sessionDir, 'logs.jsonl');
41
+
42
+ if (!existsSync(sessionFile) || !existsSync(logsFile)) {
43
+ throw new Error('Session files not found');
44
+ }
45
+
46
+ // Create zip in temp directory
47
+ const tempZip = join(tmpdir(), `agentuity-report-${randomBytes(8).toString('hex')}.zip`);
48
+
49
+ const zip = new AdmZip();
50
+ zip.addLocalFile(sessionFile);
51
+ zip.addLocalFile(logsFile);
52
+ zip.writeZip(tempZip);
53
+
54
+ return tempZip;
55
+ }
56
+
57
+ /**
58
+ * Upload the zip file to S3 using presigned URL
59
+ */
60
+ async function uploadReport(
61
+ presignedUrl: string,
62
+ zipPath: string,
63
+ logger: import('../../types').Logger
64
+ ): Promise<void> {
65
+ const fileBuffer = readFileSync(zipPath);
66
+
67
+ const response = await fetch(presignedUrl, {
68
+ method: 'PUT',
69
+ body: fileBuffer,
70
+ headers: {
71
+ 'Content-Type': 'application/zip',
72
+ 'Content-Length': fileBuffer.length.toString(),
73
+ },
74
+ });
75
+
76
+ if (!response.ok) {
77
+ const errorText = await response.text();
78
+ logger.error('Upload failed', { status: response.status, error: errorText });
79
+ throw new Error(`Upload failed: ${response.statusText}`);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create GitHub issue URL with pre-filled template
85
+ */
86
+ interface SessionData {
87
+ cli?: { version?: string };
88
+ system?: { bunVersion?: string; platform?: string; arch?: string };
89
+ command?: string;
90
+ }
91
+
92
+ function createGitHubIssueUrl(
93
+ description: string,
94
+ reportUrl: string,
95
+ reportId: string,
96
+ sessionData: SessionData
97
+ ): string {
98
+ const title = 'CLI Issue Report';
99
+
100
+ const body = `## Description
101
+
102
+ ${description}
103
+
104
+ ## Environment
105
+
106
+ - **CLI Version**: ${sessionData.cli?.version || 'unknown'}
107
+ - **Bun Version**: ${sessionData.system?.bunVersion || 'unknown'}
108
+ - **Platform**: ${sessionData.system?.platform || 'unknown'} (${sessionData.system?.arch || 'unknown'})
109
+
110
+ ## Report
111
+
112
+ - **Report ID**: \`${reportId}\`
113
+ - **Report Logs**: ${reportUrl}
114
+ - **Command**: \`${sessionData.command || 'unknown'}\`
115
+
116
+ ## Additional Context
117
+
118
+ <!-- Add any additional context or screenshots here -->
119
+
120
+ ---
121
+
122
+ **Automated report generated by \`agentuity support report\`**
123
+ `;
124
+
125
+ const params = new URLSearchParams({
126
+ title,
127
+ body,
128
+ labels: 'cli,bug',
129
+ });
130
+
131
+ return `https://github.com/agentuity/sdk/issues/new?${params.toString()}`;
132
+ }
133
+
134
+ /**
135
+ * Open URL in default browser
136
+ */
137
+ async function openBrowser(url: string, logger: import('../../types').Logger): Promise<void> {
138
+ try {
139
+ // Use platform-specific command to open URL
140
+ const platform = process.platform;
141
+ let command: string;
142
+ let args: string[];
143
+
144
+ if (platform === 'darwin') {
145
+ command = 'open';
146
+ args = [url];
147
+ } else if (platform === 'win32') {
148
+ command = 'cmd';
149
+ args = ['/c', 'start', url];
150
+ } else {
151
+ // Linux/Unix
152
+ command = 'xdg-open';
153
+ args = [url];
154
+ }
155
+
156
+ const proc = Bun.spawn([command, ...args], {
157
+ stdout: 'ignore',
158
+ stderr: 'ignore',
159
+ });
160
+
161
+ await proc.exited;
162
+
163
+ if (proc.exitCode !== 0) {
164
+ throw new Error(`Browser process exited with code ${proc.exitCode}`);
165
+ }
166
+ } catch (error) {
167
+ logger.error('Failed to open browser', { error });
168
+ throw new Error('Failed to open browser. Please open the URL manually.');
169
+ }
170
+ }
171
+
172
+ export default createSubcommand({
173
+ name: 'report',
174
+ description: 'Create a support ticket with CLI logs',
175
+ requires: {
176
+ auth: true,
177
+ apiClient: true,
178
+ },
179
+ schema: {
180
+ args: argsSchema,
181
+ options: optionsSchema,
182
+ },
183
+ handler: async (ctx) => {
184
+ const { opts, logger, apiClient } = ctx;
185
+ const isJsonMode = ctx.options.json;
186
+
187
+ // Get the latest log session
188
+ const sessionDir = getLatestLogSession();
189
+ if (!sessionDir) {
190
+ if (isJsonMode) {
191
+ console.log(JSON.stringify({ success: false, error: 'No CLI logs found' }));
192
+ } else {
193
+ tui.error('No CLI logs found');
194
+ tui.info('Run a command first to generate logs, then create a support ticket.');
195
+ }
196
+ return;
197
+ }
198
+
199
+ // Read session data to get CLI version
200
+ const sessionFile = join(sessionDir, 'session.json');
201
+ const sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8'));
202
+ const cliVersion = sessionData.cli?.version || 'unknown';
203
+
204
+ // Get issue description from:
205
+ // 1. --description flag
206
+ // 2. stdin (if piped/not a TTY)
207
+ // 3. Interactive prompt (if TTY)
208
+ let description = opts.description;
209
+
210
+ if (!description && !process.stdin.isTTY) {
211
+ // Read description from stdin (piped input)
212
+ try {
213
+ const chunks: Buffer[] = [];
214
+ for await (const chunk of process.stdin) {
215
+ chunks.push(chunk);
216
+ }
217
+ description = Buffer.concat(chunks).toString('utf-8').trim();
218
+ } catch (error) {
219
+ logger.trace('Failed to read stdin', { error });
220
+ }
221
+ }
222
+
223
+ if (!description && !isJsonMode && process.stdin.isTTY) {
224
+ // Prompt user for description
225
+ tui.info(tui.bold('Create a CLI Bug Report'));
226
+ tui.newline();
227
+ tui.output(
228
+ 'Please describe the issue you encountered. This will be included in the GitHub issue.'
229
+ );
230
+ tui.output(tui.muted('(Press Enter twice when done, or Ctrl+C to cancel)'));
231
+ tui.newline();
232
+
233
+ const { createPrompt } = tui;
234
+ const prompt = createPrompt();
235
+
236
+ try {
237
+ description = await prompt.text({
238
+ message: 'Issue description:',
239
+ hint: 'Describe what happened and what you expected...',
240
+ });
241
+
242
+ // Cleanup stdin after prompt
243
+ if (process.stdin.isTTY) {
244
+ process.stdin.pause();
245
+ }
246
+ } catch (error) {
247
+ // User cancelled
248
+ logger.trace('User cancelled report', { error });
249
+ if (!isJsonMode) {
250
+ tui.warning('Report cancelled');
251
+ }
252
+ return;
253
+ }
254
+ }
255
+
256
+ if (!description || description.trim() === '') {
257
+ if (isJsonMode) {
258
+ console.log(JSON.stringify({ success: false, error: 'Description is required' }));
259
+ } else {
260
+ tui.error(
261
+ 'Description is required. Use --description flag or pipe input via stdin.'
262
+ );
263
+ }
264
+ return;
265
+ }
266
+
267
+ try {
268
+ // Step 1: Create presigned upload URL
269
+ if (!isJsonMode) {
270
+ tui.info('Creating upload URL...');
271
+ }
272
+
273
+ const uploadResponse = await apiClient.request(
274
+ 'POST',
275
+ '/cli/support/upload',
276
+ ReportUploadResponseSchema,
277
+ {
278
+ cliVersion,
279
+ platform: sessionData.system?.platform || 'unknown',
280
+ }
281
+ );
282
+
283
+ // Debug: log the response
284
+ logger.debug('Upload response received', { uploadResponse });
285
+
286
+ if (!uploadResponse.success) {
287
+ const errorMsg = uploadResponse.message || 'Failed to create upload URL';
288
+ logger.error('Upload URL creation failed', { uploadResponse, errorMsg });
289
+ throw new Error(errorMsg);
290
+ }
291
+
292
+ const { presigned_url, url: reportUrl, report_id: reportId } = uploadResponse.data;
293
+
294
+ // Step 2: Create zip file
295
+ if (!isJsonMode) {
296
+ tui.info('Creating report archive...');
297
+ }
298
+
299
+ const zipPath = await createReportZip(sessionDir);
300
+
301
+ // Step 3: Upload to S3
302
+ if (!isJsonMode) {
303
+ tui.info('Uploading report...');
304
+ }
305
+
306
+ await uploadReport(presigned_url, zipPath, logger);
307
+
308
+ // Step 4: Create GitHub issue URL
309
+ const githubUrl = createGitHubIssueUrl(description, reportUrl, reportId, sessionData);
310
+
311
+ if (isJsonMode) {
312
+ console.log(
313
+ JSON.stringify({
314
+ success: true,
315
+ data: {
316
+ report_url: reportUrl,
317
+ github_url: githubUrl,
318
+ },
319
+ })
320
+ );
321
+ } else {
322
+ tui.newline();
323
+ tui.success('Report created successfully!');
324
+ tui.newline();
325
+ tui.output(`Report URL: ${tui.colorPrimary(reportUrl)}`);
326
+ tui.output(`GitHub Issue: ${tui.colorInfo(githubUrl)}`);
327
+ tui.newline();
328
+
329
+ if (!opts.noOpen) {
330
+ tui.info('Opening GitHub issue in browser...');
331
+ try {
332
+ await openBrowser(githubUrl, logger);
333
+ tui.newline();
334
+ tui.output(
335
+ tui.muted(
336
+ 'Please review the pre-filled issue and click "Submit new issue" when ready.'
337
+ )
338
+ );
339
+ } catch {
340
+ tui.warning('Could not open browser automatically');
341
+ tui.output('Please open this URL in your browser:');
342
+ tui.output(tui.link(githubUrl));
343
+ }
344
+ } else {
345
+ tui.output('To create the GitHub issue, open:');
346
+ tui.output(tui.link(githubUrl));
347
+ }
348
+ }
349
+ } catch (error) {
350
+ if (isJsonMode) {
351
+ console.log(
352
+ JSON.stringify({
353
+ success: false,
354
+ error: error instanceof Error ? error.message : 'Failed to create report',
355
+ })
356
+ );
357
+ } else {
358
+ tui.error('Failed to create report');
359
+ tui.output(error instanceof Error ? error.message : 'Unknown error');
360
+ }
361
+ logger.fatal('Failed to create report', { error });
362
+ }
363
+ },
364
+ });
@@ -0,0 +1,130 @@
1
+ import { createSubcommand } from '../../types';
2
+ import { z } from 'zod';
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { join } from 'node:path';
5
+ import { platform, arch, cpus, totalmem, homedir } from 'node:os';
6
+ import { getLatestLogSession } from '../../internal-logger';
7
+ import * as tui from '../../tui';
8
+ import { getVersion, getPackageName } from '../../version';
9
+ import { getAuth } from '../../config';
10
+
11
+ const argsSchema = z.object({});
12
+
13
+ const optionsSchema = z.object({});
14
+
15
+ /**
16
+ * Format bytes to human-readable string
17
+ */
18
+ function formatBytes(bytes: number): string {
19
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
20
+ let size = bytes;
21
+ let unitIndex = 0;
22
+
23
+ while (size >= 1024 && unitIndex < units.length - 1) {
24
+ size /= 1024;
25
+ unitIndex++;
26
+ }
27
+
28
+ return `${size.toFixed(2)} ${units[unitIndex]}`;
29
+ }
30
+
31
+ /**
32
+ * Get user ID from latest session
33
+ */
34
+ function getUserId(): string | null {
35
+ const sessionDir = getLatestLogSession();
36
+ if (!sessionDir) {
37
+ return null;
38
+ }
39
+
40
+ const sessionFile = join(sessionDir, 'session.json');
41
+ if (!existsSync(sessionFile)) {
42
+ return null;
43
+ }
44
+
45
+ try {
46
+ const sessionData = JSON.parse(readFileSync(sessionFile, 'utf-8'));
47
+ return sessionData.userId || null;
48
+ } catch {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ export default createSubcommand({
54
+ name: 'system',
55
+ description: 'Display system information for support',
56
+ schema: {
57
+ args: argsSchema,
58
+ options: optionsSchema,
59
+ },
60
+ handler: async (ctx) => {
61
+ const isJsonMode = ctx.options.json;
62
+
63
+ // Get userId from auth (keychain or config)
64
+ let userId = 'not authenticated';
65
+ try {
66
+ const auth = await getAuth();
67
+ if (auth?.userId) {
68
+ userId = auth.userId;
69
+ }
70
+ } catch {
71
+ // If that fails, try from session
72
+ const sessionUserId = getUserId();
73
+ if (sessionUserId) {
74
+ userId = sessionUserId;
75
+ }
76
+ }
77
+
78
+ // Gather system information
79
+ const systemInfo = {
80
+ cli: {
81
+ name: getPackageName(),
82
+ version: getVersion(),
83
+ },
84
+ bun: {
85
+ path: process.execPath || 'unknown',
86
+ version: Bun.version || process.version,
87
+ },
88
+ system: {
89
+ platform: platform(),
90
+ arch: arch(),
91
+ cpus: cpus().length,
92
+ memory: totalmem(),
93
+ memoryFormatted: formatBytes(totalmem()),
94
+ },
95
+ paths: {
96
+ cwd: process.cwd(),
97
+ home: homedir(),
98
+ configDir: join(homedir(), '.config', 'agentuity'),
99
+ },
100
+ user: {
101
+ userId: userId,
102
+ },
103
+ };
104
+
105
+ if (isJsonMode) {
106
+ tui.json(systemInfo);
107
+ } else {
108
+ // Display in vertical table format
109
+ tui.info(tui.bold('System Information'));
110
+ tui.newline();
111
+
112
+ // Flatten the data for table display
113
+ const tableData = [
114
+ { Property: 'CLI Name', Value: systemInfo.cli.name },
115
+ { Property: 'CLI Version', Value: systemInfo.cli.version },
116
+ { Property: 'Bun Path', Value: systemInfo.bun.path },
117
+ { Property: 'Bun Version', Value: systemInfo.bun.version },
118
+ { Property: 'Platform', Value: systemInfo.system.platform },
119
+ { Property: 'Architecture', Value: systemInfo.system.arch },
120
+ { Property: 'CPUs', Value: String(systemInfo.system.cpus) },
121
+ { Property: 'Memory', Value: systemInfo.system.memoryFormatted },
122
+ { Property: 'Home Directory', Value: systemInfo.paths.home },
123
+ { Property: 'Config Directory', Value: systemInfo.paths.configDir },
124
+ { Property: 'User ID', Value: systemInfo.user.userId },
125
+ ];
126
+
127
+ tui.table(tableData, ['Property', 'Value'], { layout: 'vertical' });
128
+ }
129
+ },
130
+ });
@@ -11,6 +11,7 @@ export const command = createCommand({
11
11
  name: 'version',
12
12
  description: 'Display version information',
13
13
  skipUpgradeCheck: true,
14
+ skipInternalLogging: true,
14
15
  tags: ['read-only', 'fast'],
15
16
  examples: [
16
17
  { command: getCommand('version'), description: 'Show the CLI semantic version' },